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_skia.h"
6
7#include "base/command_line.h"
8#include "base/logging.h"
9#include "base/threading/simple_thread.h"
10#include "testing/gtest/include/gtest/gtest.h"
11#include "third_party/skia/include/core/SkBitmap.h"
12#include "ui/gfx/image/image_skia_rep.h"
13#include "ui/gfx/image/image_skia_source.h"
14#include "ui/gfx/size.h"
15#include "ui/gfx/switches.h"
16
17// Duplicated from base/threading/non_thread_safe.h so that we can be
18// good citizens there and undef the macro.
19#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
20#define ENABLE_NON_THREAD_SAFE 1
21#else
22#define ENABLE_NON_THREAD_SAFE 0
23#endif
24
25namespace gfx {
26
27namespace {
28
29class FixedSource : public ImageSkiaSource {
30 public:
31  FixedSource(const ImageSkiaRep& image) : image_(image) {}
32
33  virtual ~FixedSource() {
34  }
35
36  virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
37    return image_;
38  }
39
40 private:
41  ImageSkiaRep image_;
42
43  DISALLOW_COPY_AND_ASSIGN(FixedSource);
44};
45
46class DynamicSource : public ImageSkiaSource {
47 public:
48  DynamicSource(const gfx::Size& size)
49      : size_(size),
50        last_requested_scale_(0.0f) {}
51
52  virtual ~DynamicSource() {
53  }
54
55  virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
56    last_requested_scale_ = scale;
57    return gfx::ImageSkiaRep(size_, scale);
58  }
59
60  float GetLastRequestedScaleAndReset() {
61    float result = last_requested_scale_;
62    last_requested_scale_ = 0.0f;
63    return result;
64  }
65
66 private:
67  gfx::Size size_;
68  float last_requested_scale_;
69
70  DISALLOW_COPY_AND_ASSIGN(DynamicSource);
71};
72
73class NullSource: public ImageSkiaSource {
74 public:
75  NullSource() {
76  }
77
78  virtual ~NullSource() {
79  }
80
81  virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
82    return gfx::ImageSkiaRep();
83  }
84
85 private:
86  DISALLOW_COPY_AND_ASSIGN(NullSource);
87};
88
89}  // namespace
90
91namespace test {
92class TestOnThread : public base::SimpleThread {
93 public:
94  explicit TestOnThread(ImageSkia* image_skia)
95      : SimpleThread("image_skia_on_thread"),
96        image_skia_(image_skia),
97        can_read_(false),
98        can_modify_(false) {
99  }
100
101  virtual void Run() OVERRIDE {
102    can_read_ = image_skia_->CanRead();
103    can_modify_ = image_skia_->CanModify();
104    if (can_read_)
105      image_skia_->image_reps();
106  }
107
108  void StartAndJoin() {
109    Start();
110    Join();
111  }
112
113  bool can_read() const { return can_read_; }
114
115  bool can_modify() const { return can_modify_; }
116
117 private:
118  ImageSkia* image_skia_;
119
120  bool can_read_;
121  bool can_modify_;
122
123  DISALLOW_COPY_AND_ASSIGN(TestOnThread);
124};
125
126}  // namespace test
127
128class ImageSkiaTest : public testing::Test {
129 public:
130  ImageSkiaTest() {
131    // In the test, we assume that we support 1.0f and 2.0f DSFs.
132    old_scales_ = ImageSkia::GetSupportedScales();
133
134    // Sets the list of scale factors supported by resource bundle.
135    std::vector<float> supported_scales;
136    supported_scales.push_back(1.0f);
137    supported_scales.push_back(2.0f);
138    ImageSkia::SetSupportedScales(supported_scales);
139  }
140  virtual ~ImageSkiaTest() {
141    ImageSkia::SetSupportedScales(old_scales_);
142  }
143
144 private:
145  std::vector<float> old_scales_;
146  DISALLOW_COPY_AND_ASSIGN(ImageSkiaTest);
147};
148
149TEST_F(ImageSkiaTest, FixedSource) {
150  ImageSkiaRep image(Size(100, 200), 1.0f);
151  ImageSkia image_skia(new FixedSource(image), Size(100, 200));
152  EXPECT_EQ(0U, image_skia.image_reps().size());
153
154  const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f);
155  EXPECT_EQ(100, result_100p.GetWidth());
156  EXPECT_EQ(200, result_100p.GetHeight());
157  EXPECT_EQ(1.0f, result_100p.scale());
158  EXPECT_EQ(1U, image_skia.image_reps().size());
159
160  const ImageSkiaRep& result_200p = image_skia.GetRepresentation(2.0f);
161
162  EXPECT_EQ(100, result_200p.GetWidth());
163  EXPECT_EQ(200, result_200p.GetHeight());
164  EXPECT_EQ(100, result_200p.pixel_width());
165  EXPECT_EQ(200, result_200p.pixel_height());
166  EXPECT_EQ(1.0f, result_200p.scale());
167  EXPECT_EQ(1U, image_skia.image_reps().size());
168
169  // Get the representation again and make sure it doesn't
170  // generate new image skia rep.
171  image_skia.GetRepresentation(1.0f);
172  image_skia.GetRepresentation(2.0f);
173  EXPECT_EQ(1U, image_skia.image_reps().size());
174}
175
176TEST_F(ImageSkiaTest, DynamicSource) {
177  ImageSkia image_skia(new DynamicSource(Size(100, 200)), Size(100, 200));
178  EXPECT_EQ(0U, image_skia.image_reps().size());
179  const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f);
180  EXPECT_EQ(100, result_100p.GetWidth());
181  EXPECT_EQ(200, result_100p.GetHeight());
182  EXPECT_EQ(1.0f, result_100p.scale());
183  EXPECT_EQ(1U, image_skia.image_reps().size());
184
185  const ImageSkiaRep& result_200p =
186      image_skia.GetRepresentation(2.0f);
187  EXPECT_EQ(100, result_200p.GetWidth());
188  EXPECT_EQ(200, result_200p.GetHeight());
189  EXPECT_EQ(200, result_200p.pixel_width());
190  EXPECT_EQ(400, result_200p.pixel_height());
191  EXPECT_EQ(2.0f, result_200p.scale());
192  EXPECT_EQ(2U, image_skia.image_reps().size());
193
194  // Get the representation again and make sure it doesn't
195  // generate new image skia rep.
196  image_skia.GetRepresentation(1.0f);
197  EXPECT_EQ(2U, image_skia.image_reps().size());
198  image_skia.GetRepresentation(2.0f);
199  EXPECT_EQ(2U, image_skia.image_reps().size());
200}
201
202// Tests that image_reps returns all of the representations in the
203// image when there are multiple representations for a scale factor.
204// This currently is the case with ImageLoader::LoadImages.
205TEST_F(ImageSkiaTest, ManyRepsPerScaleFactor) {
206  const int kSmallIcon1x = 16;
207  const int kSmallIcon2x = 32;
208  const int kLargeIcon1x = 32;
209
210  ImageSkia image(new NullSource(), gfx::Size(kSmallIcon1x, kSmallIcon1x));
211  // Simulate a source which loads images on a delay. Upon
212  // GetImageForScaleFactor, it immediately returns null and starts loading
213  // image reps slowly.
214  image.GetRepresentation(1.0f);
215  image.GetRepresentation(2.0f);
216
217  // After a lengthy amount of simulated time, finally loaded image reps.
218  image.AddRepresentation(ImageSkiaRep(
219      gfx::Size(kSmallIcon1x, kSmallIcon1x), 1.0f));
220  image.AddRepresentation(ImageSkiaRep(
221      gfx::Size(kSmallIcon2x, kSmallIcon2x), 2.0f));
222  image.AddRepresentation(ImageSkiaRep(
223      gfx::Size(kLargeIcon1x, kLargeIcon1x), 1.0f));
224
225  std::vector<ImageSkiaRep> image_reps = image.image_reps();
226  EXPECT_EQ(3u, image_reps.size());
227
228  int num_1x = 0;
229  int num_2x = 0;
230  for (size_t i = 0; i < image_reps.size(); ++i) {
231    if (image_reps[i].scale() == 1.0f)
232      num_1x++;
233    else if (image_reps[i].scale() == 2.0f)
234      num_2x++;
235  }
236  EXPECT_EQ(2, num_1x);
237  EXPECT_EQ(1, num_2x);
238}
239
240TEST_F(ImageSkiaTest, GetBitmap) {
241  ImageSkia image_skia(new DynamicSource(Size(100, 200)), Size(100, 200));
242  const SkBitmap* bitmap = image_skia.bitmap();
243  EXPECT_NE(static_cast<SkBitmap*>(NULL), bitmap);
244  EXPECT_FALSE(bitmap->isNull());
245}
246
247TEST_F(ImageSkiaTest, GetBitmapFromEmpty) {
248  // Create an image with 1 representation and remove it so the ImageSkiaStorage
249  // is left with no representations.
250  ImageSkia empty_image(ImageSkiaRep(Size(100, 200), 1.0f));
251  ImageSkia empty_image_copy(empty_image);
252  empty_image.RemoveRepresentation(1.0f);
253
254  // Check that ImageSkia::bitmap() still returns a valid SkBitmap pointer for
255  // the image and all its copies.
256  const SkBitmap* bitmap = empty_image_copy.bitmap();
257  ASSERT_NE(static_cast<SkBitmap*>(NULL), bitmap);
258  EXPECT_TRUE(bitmap->isNull());
259  EXPECT_TRUE(bitmap->empty());
260}
261
262TEST_F(ImageSkiaTest, BackedBySameObjectAs) {
263  // Null images should all be backed by the same object (NULL).
264  ImageSkia image;
265  ImageSkia unrelated;
266  EXPECT_TRUE(image.BackedBySameObjectAs(unrelated));
267
268  image.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(10, 10),
269                                            1.0f));
270  ImageSkia copy = image;
271  copy.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(10, 10),
272                                           2.0f));
273  unrelated.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(10, 10),
274                                                1.0f));
275  EXPECT_TRUE(image.BackedBySameObjectAs(copy));
276  EXPECT_FALSE(image.BackedBySameObjectAs(unrelated));
277  EXPECT_FALSE(copy.BackedBySameObjectAs(unrelated));
278}
279
280#if ENABLE_NON_THREAD_SAFE
281TEST_F(ImageSkiaTest, EmptyOnThreadTest) {
282  ImageSkia empty;
283  test::TestOnThread empty_on_thread(&empty);
284  empty_on_thread.Start();
285  empty_on_thread.Join();
286  EXPECT_TRUE(empty_on_thread.can_read());
287  EXPECT_TRUE(empty_on_thread.can_modify());
288}
289
290TEST_F(ImageSkiaTest, StaticOnThreadTest) {
291  ImageSkia image(ImageSkiaRep(Size(100, 200), 1.0f));
292  EXPECT_FALSE(image.IsThreadSafe());
293
294  test::TestOnThread image_on_thread(&image);
295  // an image that was never accessed on this thread can be
296  // read by other thread.
297  image_on_thread.StartAndJoin();
298  EXPECT_TRUE(image_on_thread.can_read());
299  EXPECT_TRUE(image_on_thread.can_modify());
300  EXPECT_FALSE(image.CanRead());
301  EXPECT_FALSE(image.CanModify());
302
303  image.DetachStorageFromThread();
304  // An image is accessed by this thread,
305  // so other thread cannot read/modify it.
306  image.image_reps();
307  test::TestOnThread image_on_thread2(&image);
308  image_on_thread2.StartAndJoin();
309  EXPECT_FALSE(image_on_thread2.can_read());
310  EXPECT_FALSE(image_on_thread2.can_modify());
311  EXPECT_TRUE(image.CanRead());
312  EXPECT_TRUE(image.CanModify());
313
314  image.DetachStorageFromThread();
315  scoped_ptr<ImageSkia> deep_copy(image.DeepCopy());
316  EXPECT_FALSE(deep_copy->IsThreadSafe());
317  test::TestOnThread deepcopy_on_thread(deep_copy.get());
318  deepcopy_on_thread.StartAndJoin();
319  EXPECT_TRUE(deepcopy_on_thread.can_read());
320  EXPECT_TRUE(deepcopy_on_thread.can_modify());
321  EXPECT_FALSE(deep_copy->CanRead());
322  EXPECT_FALSE(deep_copy->CanModify());
323
324  scoped_ptr<ImageSkia> deep_copy2(image.DeepCopy());
325  EXPECT_EQ(1U, deep_copy2->image_reps().size());
326  // Access it from current thread so that it can't be
327  // accessed from another thread.
328  deep_copy2->image_reps();
329  EXPECT_FALSE(deep_copy2->IsThreadSafe());
330  test::TestOnThread deepcopy2_on_thread(deep_copy2.get());
331  deepcopy2_on_thread.StartAndJoin();
332  EXPECT_FALSE(deepcopy2_on_thread.can_read());
333  EXPECT_FALSE(deepcopy2_on_thread.can_modify());
334  EXPECT_TRUE(deep_copy2->CanRead());
335  EXPECT_TRUE(deep_copy2->CanModify());
336
337  image.DetachStorageFromThread();
338  image.SetReadOnly();
339  // A read-only ImageSkia with no source is thread safe.
340  EXPECT_TRUE(image.IsThreadSafe());
341  test::TestOnThread readonly_on_thread(&image);
342  readonly_on_thread.StartAndJoin();
343  EXPECT_TRUE(readonly_on_thread.can_read());
344  EXPECT_FALSE(readonly_on_thread.can_modify());
345  EXPECT_TRUE(image.CanRead());
346  EXPECT_FALSE(image.CanModify());
347
348  image.DetachStorageFromThread();
349  image.MakeThreadSafe();
350  EXPECT_TRUE(image.IsThreadSafe());
351  test::TestOnThread threadsafe_on_thread(&image);
352  threadsafe_on_thread.StartAndJoin();
353  EXPECT_TRUE(threadsafe_on_thread.can_read());
354  EXPECT_FALSE(threadsafe_on_thread.can_modify());
355  EXPECT_TRUE(image.CanRead());
356  EXPECT_FALSE(image.CanModify());
357}
358
359TEST_F(ImageSkiaTest, SourceOnThreadTest) {
360  ImageSkia image(new DynamicSource(Size(100, 200)), Size(100, 200));
361  EXPECT_FALSE(image.IsThreadSafe());
362
363  test::TestOnThread image_on_thread(&image);
364  image_on_thread.StartAndJoin();
365  // an image that was never accessed on this thread can be
366  // read by other thread.
367  EXPECT_TRUE(image_on_thread.can_read());
368  EXPECT_TRUE(image_on_thread.can_modify());
369  EXPECT_FALSE(image.CanRead());
370  EXPECT_FALSE(image.CanModify());
371
372  image.DetachStorageFromThread();
373  // An image is accessed by this thread,
374  // so other thread cannot read/modify it.
375  image.image_reps();
376  test::TestOnThread image_on_thread2(&image);
377  image_on_thread2.StartAndJoin();
378  EXPECT_FALSE(image_on_thread2.can_read());
379  EXPECT_FALSE(image_on_thread2.can_modify());
380  EXPECT_TRUE(image.CanRead());
381  EXPECT_TRUE(image.CanModify());
382
383  image.DetachStorageFromThread();
384  image.SetReadOnly();
385  EXPECT_FALSE(image.IsThreadSafe());
386  test::TestOnThread readonly_on_thread(&image);
387  readonly_on_thread.StartAndJoin();
388  EXPECT_TRUE(readonly_on_thread.can_read());
389  EXPECT_FALSE(readonly_on_thread.can_modify());
390  EXPECT_FALSE(image.CanRead());
391  EXPECT_FALSE(image.CanModify());
392
393  image.DetachStorageFromThread();
394  image.MakeThreadSafe();
395  EXPECT_TRUE(image.IsThreadSafe());
396  // Check if image reps are generated for supported scale factors.
397  EXPECT_EQ(ImageSkia::GetSupportedScales().size(),
398           image.image_reps().size());
399  test::TestOnThread threadsafe_on_thread(&image);
400  threadsafe_on_thread.StartAndJoin();
401  EXPECT_TRUE(threadsafe_on_thread.can_read());
402  EXPECT_FALSE(threadsafe_on_thread.can_modify());
403  EXPECT_TRUE(image.CanRead());
404  EXPECT_FALSE(image.CanModify());
405}
406#endif  // ENABLE_NON_THREAD_SAFE
407
408// Just in case we ever get lumped together with other compilation units.
409#undef ENABLE_NON_THREAD_SAFE
410
411TEST_F(ImageSkiaTest, Unscaled) {
412  SkBitmap bitmap;
413
414  // An ImageSkia created with 1x bitmap is unscaled.
415  ImageSkia image_skia = ImageSkia::CreateFrom1xBitmap(bitmap);
416  EXPECT_TRUE(image_skia.GetRepresentation(1.0f).unscaled());
417  ImageSkiaRep rep_2x(Size(100, 100), 2.0f);
418
419  // When reps for other scales are added, the unscaled image
420  // becomes scaled.
421  image_skia.AddRepresentation(rep_2x);
422  EXPECT_FALSE(image_skia.GetRepresentation(1.0f).unscaled());
423  EXPECT_FALSE(image_skia.GetRepresentation(2.0f).unscaled());
424}
425
426namespace {
427
428std::vector<float> GetSortedScaleFactors(const gfx::ImageSkia& image) {
429  const std::vector<ImageSkiaRep>& image_reps = image.image_reps();
430  std::vector<float> scale_factors;
431  for (size_t i = 0; i < image_reps.size(); ++i) {
432    scale_factors.push_back(image_reps[i].scale());
433  }
434  std::sort(scale_factors.begin(), scale_factors.end());
435  return scale_factors;
436}
437
438}  // namespace
439
440TEST_F(ImageSkiaTest, ArbitraryScaleFactor) {
441  // Do not test if the ImageSkia doesn't support arbitrary scale factors.
442  if (!ImageSkia::IsDSFScalingInImageSkiaEnabled())
443    return;
444
445  // source is owned by |image|
446  DynamicSource* source = new DynamicSource(Size(100, 200));
447  ImageSkia image(source, gfx::Size(100, 200));
448
449  image.GetRepresentation(1.5f);
450  EXPECT_EQ(2.0f, source->GetLastRequestedScaleAndReset());
451  std::vector<ImageSkiaRep> image_reps = image.image_reps();
452  EXPECT_EQ(2u, image_reps.size());
453
454  std::vector<float> scale_factors = GetSortedScaleFactors(image);
455  EXPECT_EQ(1.5f, scale_factors[0]);
456  EXPECT_EQ(2.0f, scale_factors[1]);
457
458  // Requesting 1.75 scale factor also falls back to 2.0f and rescale.
459  // However, the image already has the 2.0f data, so it won't fetch again.
460  image.GetRepresentation(1.75f);
461  EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset());
462  image_reps = image.image_reps();
463  EXPECT_EQ(3u, image_reps.size());
464
465  scale_factors = GetSortedScaleFactors(image);
466  EXPECT_EQ(1.5f, scale_factors[0]);
467  EXPECT_EQ(1.75f, scale_factors[1]);
468  EXPECT_EQ(2.0f, scale_factors[2]);
469
470  // Requesting 1.25 scale factor also falls back to 2.0f and rescale.
471  // However, the image already has the 2.0f data, so it won't fetch again.
472  image.GetRepresentation(1.25f);
473  EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset());
474  image_reps = image.image_reps();
475  EXPECT_EQ(4u, image_reps.size());
476  scale_factors = GetSortedScaleFactors(image);
477  EXPECT_EQ(1.25f, scale_factors[0]);
478  EXPECT_EQ(1.5f, scale_factors[1]);
479  EXPECT_EQ(1.75f, scale_factors[2]);
480  EXPECT_EQ(2.0f, scale_factors[3]);
481
482  // 1.20 is falled back to 1.0.
483  image.GetRepresentation(1.20f);
484  EXPECT_EQ(1.0f, source->GetLastRequestedScaleAndReset());
485  image_reps = image.image_reps();
486  EXPECT_EQ(6u, image_reps.size());
487  scale_factors = GetSortedScaleFactors(image);
488  EXPECT_EQ(1.0f, scale_factors[0]);
489  EXPECT_EQ(1.2f, scale_factors[1]);
490  EXPECT_EQ(1.25f, scale_factors[2]);
491  EXPECT_EQ(1.5f, scale_factors[3]);
492  EXPECT_EQ(1.75f, scale_factors[4]);
493  EXPECT_EQ(2.0f, scale_factors[5]);
494
495  // Scale factor less than 1.0f will be falled back to 1.0f
496  image.GetRepresentation(0.75f);
497  EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset());
498  image_reps = image.image_reps();
499  EXPECT_EQ(7u, image_reps.size());
500
501  scale_factors = GetSortedScaleFactors(image);
502  EXPECT_EQ(0.75f, scale_factors[0]);
503  EXPECT_EQ(1.0f, scale_factors[1]);
504  EXPECT_EQ(1.2f, scale_factors[2]);
505  EXPECT_EQ(1.25f, scale_factors[3]);
506  EXPECT_EQ(1.5f, scale_factors[4]);
507  EXPECT_EQ(1.75f, scale_factors[5]);
508  EXPECT_EQ(2.0f, scale_factors[6]);
509
510  // Scale factor greater than 2.0f is falled back to 2.0f because it's not
511  // supported.
512  image.GetRepresentation(3.0f);
513  EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset());
514  image_reps = image.image_reps();
515  EXPECT_EQ(8u, image_reps.size());
516}
517
518TEST_F(ImageSkiaTest, ArbitraryScaleFactorWithMissingResource) {
519  // Do not test if the ImageSkia doesn't support arbitrary scale factors.
520  if (!ImageSkia::IsDSFScalingInImageSkiaEnabled())
521    return;
522
523  ImageSkia image(new FixedSource(
524      ImageSkiaRep(Size(100, 200), 1.0f)), Size(100, 200));
525
526  // Requesting 1.5f -- falls back to 2.0f, but couldn't find. It should
527  // look up 1.0f and then rescale it.
528  const ImageSkiaRep& rep = image.GetRepresentation(1.5f);
529  EXPECT_EQ(1.5f, rep.scale());
530  EXPECT_EQ(2U, image.image_reps().size());
531  EXPECT_EQ(1.0f, image.image_reps()[0].scale());
532  EXPECT_EQ(1.5f, image.image_reps()[1].scale());
533}
534
535TEST_F(ImageSkiaTest, UnscaledImageForArbitraryScaleFactor) {
536  // Do not test if the ImageSkia doesn't support arbitrary scale factors.
537  if (!ImageSkia::IsDSFScalingInImageSkiaEnabled())
538    return;
539
540  // 0.0f means unscaled.
541  ImageSkia image(new FixedSource(
542      ImageSkiaRep(Size(100, 200), 0.0f)), Size(100, 200));
543
544  // Requesting 2.0f, which should return 1.0f unscaled image.
545  const ImageSkiaRep& rep = image.GetRepresentation(2.0f);
546  EXPECT_EQ(1.0f, rep.scale());
547  EXPECT_EQ("100x200", rep.pixel_size().ToString());
548  EXPECT_TRUE(rep.unscaled());
549  EXPECT_EQ(1U, image.image_reps().size());
550
551  // Same for any other scale factors.
552  const ImageSkiaRep& rep15 = image.GetRepresentation(1.5f);
553  EXPECT_EQ(1.0f, rep15.scale());
554  EXPECT_EQ("100x200", rep15.pixel_size().ToString());
555  EXPECT_TRUE(rep15.unscaled());
556  EXPECT_EQ(1U, image.image_reps().size());
557
558  const ImageSkiaRep& rep12 = image.GetRepresentation(1.2f);
559  EXPECT_EQ(1.0f, rep12.scale());
560  EXPECT_EQ("100x200", rep12.pixel_size().ToString());
561  EXPECT_TRUE(rep12.unscaled());
562  EXPECT_EQ(1U, image.image_reps().size());
563}
564
565}  // namespace gfx
566