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 #include <utility>
9 #include <vector>
10
11 #include "base/logging.h"
12 #include "base/macros.h"
13 #include "base/memory/ptr_util.h"
14 #include "build/build_config.h"
15 #include "third_party/skia/include/core/SkBitmap.h"
16 #include "ui/gfx/geometry/size.h"
17 #include "ui/gfx/image/image_platform.h"
18 #include "ui/gfx/image/image_png_rep.h"
19 #include "ui/gfx/image/image_skia.h"
20
21 #if defined(OS_IOS)
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/foundation_util.h"
26 #include "base/mac/mac_util.h"
27 #include "ui/gfx/image/image_skia_util_mac.h"
28 #endif
29
30 namespace gfx {
31
32 namespace {
33
34 using RepresentationMap =
35 std::map<Image::RepresentationType, std::unique_ptr<internal::ImageRep>>;
36
37 } // namespace
38
39 namespace internal {
40
41 class ImageRepPNG;
42 class ImageRepSkia;
43 class ImageRepCocoa;
44 class ImageRepCocoaTouch;
45
46 // An ImageRep is the object that holds the backing memory for an Image. Each
47 // RepresentationType has an ImageRep subclass that is responsible for freeing
48 // the memory that the ImageRep holds. When an ImageRep is created, it expects
49 // to take ownership of the image, without having to retain it or increase its
50 // reference count.
51 class ImageRep {
52 public:
ImageRep(Image::RepresentationType rep)53 explicit ImageRep(Image::RepresentationType rep) : type_(rep) {}
54
55 // Deletes the associated pixels of an ImageRep.
~ImageRep()56 virtual ~ImageRep() {}
57
58 // Cast helpers ("fake RTTI").
AsImageRepPNG() const59 const ImageRepPNG* AsImageRepPNG() const {
60 CHECK_EQ(type_, Image::kImageRepPNG);
61 return reinterpret_cast<const ImageRepPNG*>(this);
62 }
AsImageRepPNG()63 ImageRepPNG* AsImageRepPNG() {
64 return const_cast<ImageRepPNG*>(
65 static_cast<const ImageRep*>(this)->AsImageRepPNG());
66 }
67
AsImageRepSkia() const68 const ImageRepSkia* AsImageRepSkia() const {
69 CHECK_EQ(type_, Image::kImageRepSkia);
70 return reinterpret_cast<const ImageRepSkia*>(this);
71 }
AsImageRepSkia()72 ImageRepSkia* AsImageRepSkia() {
73 return const_cast<ImageRepSkia*>(
74 static_cast<const ImageRep*>(this)->AsImageRepSkia());
75 }
76
77 #if defined(OS_IOS)
AsImageRepCocoaTouch() const78 const ImageRepCocoaTouch* AsImageRepCocoaTouch() const {
79 CHECK_EQ(type_, Image::kImageRepCocoaTouch);
80 return reinterpret_cast<const ImageRepCocoaTouch*>(this);
81 }
AsImageRepCocoaTouch()82 ImageRepCocoaTouch* AsImageRepCocoaTouch() {
83 return const_cast<ImageRepCocoaTouch*>(
84 static_cast<const ImageRep*>(this)->AsImageRepCocoaTouch());
85 }
86 #elif defined(OS_MACOSX)
AsImageRepCocoa() const87 const ImageRepCocoa* AsImageRepCocoa() const {
88 CHECK_EQ(type_, Image::kImageRepCocoa);
89 return reinterpret_cast<const ImageRepCocoa*>(this);
90 }
AsImageRepCocoa()91 ImageRepCocoa* AsImageRepCocoa() {
92 return const_cast<ImageRepCocoa*>(
93 static_cast<const ImageRep*>(this)->AsImageRepCocoa());
94 }
95 #endif
96
type() const97 Image::RepresentationType type() const { return type_; }
98
99 virtual int Width() const = 0;
100 virtual int Height() const = 0;
101 virtual gfx::Size Size() const = 0;
102
103 private:
104 Image::RepresentationType type_;
105 };
106
107 class ImageRepPNG : public ImageRep {
108 public:
ImageRepPNG()109 ImageRepPNG() : ImageRep(Image::kImageRepPNG) {
110 }
111
ImageRepPNG(const std::vector<ImagePNGRep> & image_png_reps)112 explicit ImageRepPNG(const std::vector<ImagePNGRep>& image_png_reps)
113 : ImageRep(Image::kImageRepPNG), image_png_reps_(image_png_reps) {}
114
~ImageRepPNG()115 ~ImageRepPNG() override {}
116
Width() const117 int Width() const override { return Size().width(); }
118
Height() const119 int Height() const override { return Size().height(); }
120
Size() const121 gfx::Size Size() const override {
122 // Read the PNG data to get the image size, caching it.
123 if (!size_cache_) {
124 for (auto it = image_reps().begin(); it != image_reps().end(); ++it) {
125 if (it->scale == 1.0f) {
126 size_cache_ = it->Size();
127 return *size_cache_;
128 }
129 }
130 size_cache_ = gfx::Size();
131 }
132
133 return *size_cache_;
134 }
135
image_reps() const136 const std::vector<ImagePNGRep>& image_reps() const { return image_png_reps_; }
137
138 private:
139 std::vector<ImagePNGRep> image_png_reps_;
140
141 // Cached to avoid having to parse the raw data multiple times.
142 mutable base::Optional<gfx::Size> size_cache_;
143
144 DISALLOW_COPY_AND_ASSIGN(ImageRepPNG);
145 };
146
147 class ImageRepSkia : public ImageRep {
148 public:
ImageRepSkia(ImageSkia image)149 explicit ImageRepSkia(ImageSkia image)
150 : ImageRep(Image::kImageRepSkia), image_(image) {}
151
~ImageRepSkia()152 ~ImageRepSkia() override {}
153
Width() const154 int Width() const override { return image_.width(); }
155
Height() const156 int Height() const override { return image_.height(); }
157
Size() const158 gfx::Size Size() const override { return image_.size(); }
159
image() const160 const ImageSkia* image() const { return &image_; }
image()161 ImageSkia* image() { return &image_; }
162
163 private:
164 ImageSkia image_;
165
166 DISALLOW_COPY_AND_ASSIGN(ImageRepSkia);
167 };
168
169 #if defined(OS_IOS)
170 class ImageRepCocoaTouch : public ImageRep {
171 public:
ImageRepCocoaTouch(UIImage * image)172 explicit ImageRepCocoaTouch(UIImage* image)
173 : ImageRep(Image::kImageRepCocoaTouch),
174 image_(image) {
175 CHECK(image_);
176 base::mac::NSObjectRetain(image_);
177 }
178
~ImageRepCocoaTouch()179 ~ImageRepCocoaTouch() override {
180 base::mac::NSObjectRelease(image_);
181 image_ = nil;
182 }
183
Width() const184 int Width() const override { return Size().width(); }
185
Height() const186 int Height() const override { return Size().height(); }
187
Size() const188 gfx::Size Size() const override { return internal::UIImageSize(image_); }
189
image() const190 UIImage* image() const { return image_; }
191
192 private:
193 UIImage* image_;
194
195 DISALLOW_COPY_AND_ASSIGN(ImageRepCocoaTouch);
196 };
197 #elif defined(OS_MACOSX)
198 class ImageRepCocoa : public ImageRep {
199 public:
ImageRepCocoa(NSImage * image)200 explicit ImageRepCocoa(NSImage* image)
201 : ImageRep(Image::kImageRepCocoa),
202 image_(image) {
203 CHECK(image_);
204 base::mac::NSObjectRetain(image_);
205 }
206
~ImageRepCocoa()207 ~ImageRepCocoa() override {
208 base::mac::NSObjectRelease(image_);
209 image_ = nil;
210 }
211
Width() const212 int Width() const override { return Size().width(); }
213
Height() const214 int Height() const override { return Size().height(); }
215
Size() const216 gfx::Size Size() const override { return internal::NSImageSize(image_); }
217
image() const218 NSImage* image() const { return image_; }
219
220 private:
221 NSImage* image_;
222
223 DISALLOW_COPY_AND_ASSIGN(ImageRepCocoa);
224 };
225 #endif // defined(OS_MACOSX)
226
227 // The Storage class acts similarly to the pixels in a SkBitmap: the Image
228 // class holds a refptr instance of Storage, which in turn holds all the
229 // ImageReps. This way, the Image can be cheaply copied.
230 //
231 // This class is deliberately not RefCountedThreadSafe. Making it so does not
232 // solve threading issues, as gfx::Image and its internal classes are
233 // themselves not threadsafe.
234 class ImageStorage : public base::RefCounted<ImageStorage> {
235 public:
ImageStorage(Image::RepresentationType default_type)236 explicit ImageStorage(Image::RepresentationType default_type)
237 : default_representation_type_(default_type)
238 #if defined(OS_MACOSX) && !defined(OS_IOS)
239 ,
240 default_representation_color_space_(
241 base::mac::GetGenericRGBColorSpace())
242 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
243 {
244 }
245
default_representation_type() const246 Image::RepresentationType default_representation_type() const {
247 DCHECK(IsOnValidSequence());
248 return default_representation_type_;
249 }
250
HasRepresentation(Image::RepresentationType type) const251 bool HasRepresentation(Image::RepresentationType type) const {
252 DCHECK(IsOnValidSequence());
253 return representations_.count(type) != 0;
254 }
255
RepresentationCount() const256 size_t RepresentationCount() const {
257 DCHECK(IsOnValidSequence());
258 return representations_.size();
259 }
260
GetRepresentation(Image::RepresentationType rep_type,bool must_exist) const261 const ImageRep* GetRepresentation(Image::RepresentationType rep_type,
262 bool must_exist) const {
263 DCHECK(IsOnValidSequence());
264 RepresentationMap::const_iterator it = representations_.find(rep_type);
265 if (it == representations_.end()) {
266 CHECK(!must_exist);
267 return nullptr;
268 }
269 return it->second.get();
270 }
271
AddRepresentation(std::unique_ptr<ImageRep> rep) const272 const ImageRep* AddRepresentation(std::unique_ptr<ImageRep> rep) const {
273 DCHECK(IsOnValidSequence());
274 Image::RepresentationType type = rep->type();
275 auto result = representations_.emplace(type, std::move(rep));
276
277 // insert should not fail (implies that there was already a representation
278 // of that type in the map).
279 CHECK(result.second) << "type was already in map.";
280
281 return result.first->second.get();
282 }
283
284 #if defined(OS_MACOSX) && !defined(OS_IOS)
set_default_representation_color_space(CGColorSpaceRef color_space)285 void set_default_representation_color_space(CGColorSpaceRef color_space) {
286 DCHECK(IsOnValidSequence());
287 default_representation_color_space_ = color_space;
288 }
default_representation_color_space() const289 CGColorSpaceRef default_representation_color_space() const {
290 DCHECK(IsOnValidSequence());
291 return default_representation_color_space_;
292 }
293 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
294
295 private:
296 friend class base::RefCounted<ImageStorage>;
297
~ImageStorage()298 ~ImageStorage() {}
299
300 // The type of image that was passed to the constructor. This key will always
301 // exist in the |representations_| map.
302 Image::RepresentationType default_representation_type_;
303
304 #if defined(OS_MACOSX) && !defined(OS_IOS)
305 // The default representation's colorspace. This is used for converting to
306 // NSImage. This field exists to compensate for PNGCodec not writing or
307 // reading colorspace ancillary chunks. (sRGB, iCCP).
308 // Not owned.
309 CGColorSpaceRef default_representation_color_space_;
310 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
311
312 // All the representations of an Image. Size will always be at least one, with
313 // more for any converted representations.
314 mutable RepresentationMap representations_;
315
316 DISALLOW_COPY_AND_ASSIGN(ImageStorage);
317 };
318
319 } // namespace internal
320
Image()321 Image::Image() {
322 // |storage_| is null for empty Images.
323 }
324
Image(const std::vector<ImagePNGRep> & image_reps)325 Image::Image(const std::vector<ImagePNGRep>& image_reps) {
326 // Do not store obviously invalid ImagePNGReps.
327 std::vector<ImagePNGRep> filtered;
328 for (size_t i = 0; i < image_reps.size(); ++i) {
329 if (image_reps[i].raw_data.get() && image_reps[i].raw_data->size())
330 filtered.push_back(image_reps[i]);
331 }
332
333 if (filtered.empty())
334 return;
335
336 storage_ = new internal::ImageStorage(Image::kImageRepPNG);
337 AddRepresentation(std::make_unique<internal::ImageRepPNG>(filtered));
338 }
339
Image(const ImageSkia & image)340 Image::Image(const ImageSkia& image) {
341 if (!image.isNull()) {
342 storage_ = new internal::ImageStorage(Image::kImageRepSkia);
343 AddRepresentation(std::make_unique<internal::ImageRepSkia>(image));
344 }
345 }
346
347 #if defined(OS_IOS)
Image(UIImage * image)348 Image::Image(UIImage* image) {
349 if (image) {
350 storage_ = new internal::ImageStorage(Image::kImageRepCocoaTouch);
351 AddRepresentation(std::make_unique<internal::ImageRepCocoaTouch>(image));
352 }
353 }
354 #elif defined(OS_MACOSX)
Image(NSImage * image)355 Image::Image(NSImage* image) {
356 if (image) {
357 storage_ = new internal::ImageStorage(Image::kImageRepCocoa);
358 AddRepresentation(std::make_unique<internal::ImageRepCocoa>(image));
359 }
360 }
361 #endif
362
363 Image::Image(const Image& other) = default;
364
365 Image::Image(Image&& other) noexcept = default;
366
367 Image& Image::operator=(const Image& other) = default;
368
369 Image& Image::operator=(Image&& other) noexcept = default;
370
~Image()371 Image::~Image() {}
372
373 // static
CreateFrom1xBitmap(const SkBitmap & bitmap)374 Image Image::CreateFrom1xBitmap(const SkBitmap& bitmap) {
375 return Image(ImageSkia::CreateFrom1xBitmap(bitmap));
376 }
377
378 // static
CreateFrom1xPNGBytes(const unsigned char * input,size_t input_size)379 Image Image::CreateFrom1xPNGBytes(const unsigned char* input,
380 size_t input_size) {
381 if (input_size == 0u)
382 return Image();
383
384 scoped_refptr<base::RefCountedBytes> raw_data(new base::RefCountedBytes());
385 raw_data->data().assign(input, input + input_size);
386
387 return CreateFrom1xPNGBytes(raw_data);
388 }
389
CreateFrom1xPNGBytes(const scoped_refptr<base::RefCountedMemory> & input)390 Image Image::CreateFrom1xPNGBytes(
391 const scoped_refptr<base::RefCountedMemory>& input) {
392 if (!input.get() || input->size() == 0u)
393 return Image();
394
395 std::vector<ImagePNGRep> image_reps;
396 image_reps.push_back(ImagePNGRep(input, 1.0f));
397 return Image(image_reps);
398 }
399
ToSkBitmap() const400 const SkBitmap* Image::ToSkBitmap() const {
401 // Possibly create and cache an intermediate ImageRepSkia.
402 return ToImageSkia()->bitmap();
403 }
404
ToImageSkia() const405 const ImageSkia* Image::ToImageSkia() const {
406 const internal::ImageRep* rep = GetRepresentation(kImageRepSkia, false);
407 if (!rep) {
408 std::unique_ptr<internal::ImageRep> scoped_rep;
409 switch (DefaultRepresentationType()) {
410 case kImageRepPNG: {
411 const internal::ImageRepPNG* png_rep =
412 GetRepresentation(kImageRepPNG, true)->AsImageRepPNG();
413 scoped_rep = std::make_unique<internal::ImageRepSkia>(
414 internal::ImageSkiaFromPNG(png_rep->image_reps()));
415 break;
416 }
417 #if defined(OS_IOS)
418 case kImageRepCocoaTouch: {
419 const internal::ImageRepCocoaTouch* native_rep =
420 GetRepresentation(kImageRepCocoaTouch, true)
421 ->AsImageRepCocoaTouch();
422 scoped_rep = std::make_unique<internal::ImageRepSkia>(
423 ImageSkia(ImageSkiaFromUIImage(native_rep->image())));
424 break;
425 }
426 #elif defined(OS_MACOSX)
427 case kImageRepCocoa: {
428 const internal::ImageRepCocoa* native_rep =
429 GetRepresentation(kImageRepCocoa, true)->AsImageRepCocoa();
430 scoped_rep = std::make_unique<internal::ImageRepSkia>(
431 ImageSkia(ImageSkiaFromNSImage(native_rep->image())));
432 break;
433 }
434 #endif
435 default:
436 NOTREACHED();
437 }
438 CHECK(scoped_rep);
439 rep = AddRepresentation(std::move(scoped_rep));
440 }
441 return rep->AsImageRepSkia()->image();
442 }
443
444 #if defined(OS_IOS)
ToUIImage() const445 UIImage* Image::ToUIImage() const {
446 const internal::ImageRep* rep = GetRepresentation(kImageRepCocoaTouch, false);
447 if (!rep) {
448 std::unique_ptr<internal::ImageRep> scoped_rep;
449 switch (DefaultRepresentationType()) {
450 case kImageRepPNG: {
451 const internal::ImageRepPNG* png_rep =
452 GetRepresentation(kImageRepPNG, true)->AsImageRepPNG();
453 scoped_rep = std::make_unique<internal::ImageRepCocoaTouch>(
454 internal::UIImageFromPNG(png_rep->image_reps()));
455 break;
456 }
457 case kImageRepSkia: {
458 const internal::ImageRepSkia* skia_rep =
459 GetRepresentation(kImageRepSkia, true)->AsImageRepSkia();
460 UIImage* image = UIImageFromImageSkia(*skia_rep->image());
461 scoped_rep = std::make_unique<internal::ImageRepCocoaTouch>(image);
462 break;
463 }
464 default:
465 NOTREACHED();
466 }
467 CHECK(scoped_rep);
468 rep = AddRepresentation(std::move(scoped_rep));
469 }
470 return rep->AsImageRepCocoaTouch()->image();
471 }
472 #elif defined(OS_MACOSX)
ToNSImage() const473 NSImage* Image::ToNSImage() const {
474 const internal::ImageRep* rep = GetRepresentation(kImageRepCocoa, false);
475 if (!rep) {
476 std::unique_ptr<internal::ImageRep> scoped_rep;
477 CGColorSpaceRef default_representation_color_space =
478 storage()->default_representation_color_space();
479
480 switch (DefaultRepresentationType()) {
481 case kImageRepPNG: {
482 const internal::ImageRepPNG* png_rep =
483 GetRepresentation(kImageRepPNG, true)->AsImageRepPNG();
484 scoped_rep =
485 std::make_unique<internal::ImageRepCocoa>(internal::NSImageFromPNG(
486 png_rep->image_reps(), default_representation_color_space));
487 break;
488 }
489 case kImageRepSkia: {
490 const internal::ImageRepSkia* skia_rep =
491 GetRepresentation(kImageRepSkia, true)->AsImageRepSkia();
492 NSImage* image = NSImageFromImageSkiaWithColorSpace(*skia_rep->image(),
493 default_representation_color_space);
494 scoped_rep = std::make_unique<internal::ImageRepCocoa>(image);
495 break;
496 }
497 default:
498 NOTREACHED();
499 }
500 CHECK(scoped_rep);
501 rep = AddRepresentation(std::move(scoped_rep));
502 }
503 return rep->AsImageRepCocoa()->image();
504 }
505 #endif
506
As1xPNGBytes() const507 scoped_refptr<base::RefCountedMemory> Image::As1xPNGBytes() const {
508 if (IsEmpty())
509 return new base::RefCountedBytes();
510
511 const internal::ImageRep* rep = GetRepresentation(kImageRepPNG, false);
512
513 if (rep) {
514 const std::vector<ImagePNGRep>& image_png_reps =
515 rep->AsImageRepPNG()->image_reps();
516 for (size_t i = 0; i < image_png_reps.size(); ++i) {
517 if (image_png_reps[i].scale == 1.0f)
518 return image_png_reps[i].raw_data;
519 }
520 return new base::RefCountedBytes();
521 }
522
523 scoped_refptr<base::RefCountedMemory> png_bytes;
524 switch (DefaultRepresentationType()) {
525 #if defined(OS_IOS)
526 case kImageRepCocoaTouch: {
527 const internal::ImageRepCocoaTouch* cocoa_touch_rep =
528 GetRepresentation(kImageRepCocoaTouch, true)->AsImageRepCocoaTouch();
529 png_bytes = internal::Get1xPNGBytesFromUIImage(
530 cocoa_touch_rep->image());
531 break;
532 }
533 #elif defined(OS_MACOSX)
534 case kImageRepCocoa: {
535 const internal::ImageRepCocoa* cocoa_rep =
536 GetRepresentation(kImageRepCocoa, true)->AsImageRepCocoa();
537 png_bytes = internal::Get1xPNGBytesFromNSImage(cocoa_rep->image());
538 break;
539 }
540 #endif
541 case kImageRepSkia: {
542 const internal::ImageRepSkia* skia_rep =
543 GetRepresentation(kImageRepSkia, true)->AsImageRepSkia();
544 png_bytes = internal::Get1xPNGBytesFromImageSkia(skia_rep->image());
545 break;
546 }
547 default:
548 NOTREACHED();
549 }
550 if (!png_bytes.get() || !png_bytes->size()) {
551 // Add an ImageRepPNG with no data such that the conversion is not
552 // attempted each time we want the PNG bytes.
553 AddRepresentation(base::WrapUnique(new internal::ImageRepPNG()));
554 return new base::RefCountedBytes();
555 }
556
557 // Do not insert representations for scale factors other than 1x even if
558 // they are available because:
559 // - Only the 1x PNG bytes can be accessed.
560 // - ImageRepPNG is not used as an intermediate type in converting to a
561 // final type eg (converting from ImageRepSkia to ImageRepPNG to get an
562 // ImageRepCocoa).
563 std::vector<ImagePNGRep> image_png_reps;
564 image_png_reps.push_back(ImagePNGRep(png_bytes, 1.0f));
565 AddRepresentation(
566 base::WrapUnique(new internal::ImageRepPNG(image_png_reps)));
567 return png_bytes;
568 }
569
AsBitmap() const570 SkBitmap Image::AsBitmap() const {
571 return IsEmpty() ? SkBitmap() : *ToSkBitmap();
572 }
573
AsImageSkia() const574 ImageSkia Image::AsImageSkia() const {
575 return IsEmpty() ? ImageSkia() : *ToImageSkia();
576 }
577
578 #if defined(OS_MACOSX) && !defined(OS_IOS)
AsNSImage() const579 NSImage* Image::AsNSImage() const {
580 return IsEmpty() ? nil : ToNSImage();
581 }
582 #endif
583
HasRepresentation(RepresentationType type) const584 bool Image::HasRepresentation(RepresentationType type) const {
585 return storage() && storage()->HasRepresentation(type);
586 }
587
RepresentationCount() const588 size_t Image::RepresentationCount() const {
589 return storage() ? storage()->RepresentationCount() : 0;
590 }
591
IsEmpty() const592 bool Image::IsEmpty() const {
593 return RepresentationCount() == 0;
594 }
595
Width() const596 int Image::Width() const {
597 if (IsEmpty())
598 return 0;
599 return GetRepresentation(DefaultRepresentationType(), true)->Width();
600 }
601
Height() const602 int Image::Height() const {
603 if (IsEmpty())
604 return 0;
605 return GetRepresentation(DefaultRepresentationType(), true)->Height();
606 }
607
Size() const608 gfx::Size Image::Size() const {
609 if (IsEmpty())
610 return gfx::Size();
611 return GetRepresentation(DefaultRepresentationType(), true)->Size();
612 }
613
614 #if defined(OS_MACOSX) && !defined(OS_IOS)
SetSourceColorSpace(CGColorSpaceRef color_space)615 void Image::SetSourceColorSpace(CGColorSpaceRef color_space) {
616 if (storage())
617 storage()->set_default_representation_color_space(color_space);
618 }
619 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
620
DefaultRepresentationType() const621 Image::RepresentationType Image::DefaultRepresentationType() const {
622 CHECK(storage());
623 return storage()->default_representation_type();
624 }
625
GetRepresentation(RepresentationType rep_type,bool must_exist) const626 const internal::ImageRep* Image::GetRepresentation(RepresentationType rep_type,
627 bool must_exist) const {
628 CHECK(storage());
629 return storage()->GetRepresentation(rep_type, must_exist);
630 }
631
AddRepresentation(std::unique_ptr<internal::ImageRep> rep) const632 const internal::ImageRep* Image::AddRepresentation(
633 std::unique_ptr<internal::ImageRep> rep) const {
634 CHECK(storage());
635 return storage()->AddRepresentation(std::move(rep));
636 }
637
638 } // namespace gfx
639