910e62b5创建于 1月15日历史提交
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gfx/image/image_skia.h"

#include <stddef.h>

#include "base/check_op.h"
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/threading/simple_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/resource/resource_scale_factor.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/gfx/image/image_skia_source.h"
#include "ui/gfx/switches.h"

namespace gfx {

namespace {

class FixedSource : public ImageSkiaSource {
 public:
  explicit FixedSource(const ImageSkiaRep& image) : image_(image) {}

  FixedSource(const FixedSource&) = delete;
  FixedSource& operator=(const FixedSource&) = delete;

  ~FixedSource() override {}

  ImageSkiaRep GetImageForScale(float scale) override { return image_; }

 private:
  ImageSkiaRep image_;
};

class FixedScaleSource : public ImageSkiaSource {
 public:
  explicit FixedScaleSource(const ImageSkiaRep& image) : image_(image) {}

  FixedScaleSource(const FixedScaleSource&) = delete;
  FixedScaleSource& operator=(const FixedScaleSource&) = delete;

  ~FixedScaleSource() override {}

  ImageSkiaRep GetImageForScale(float scale) override {
    if (!image_.unscaled() && image_.scale() != scale)
      return ImageSkiaRep();
    return image_;
  }

 private:
  ImageSkiaRep image_;
};

class DynamicSource : public ImageSkiaSource {
 public:
  explicit DynamicSource(const gfx::Size& size)
      : size_(size),
        last_requested_scale_(0.0f) {}

  DynamicSource(const DynamicSource&) = delete;
  DynamicSource& operator=(const DynamicSource&) = delete;

  ~DynamicSource() override {}

  ImageSkiaRep GetImageForScale(float scale) override {
    last_requested_scale_ = scale;
    return gfx::ImageSkiaRep(size_, scale);
  }

  float GetLastRequestedScaleAndReset() {
    float result = last_requested_scale_;
    last_requested_scale_ = 0.0f;
    return result;
  }

 private:
  gfx::Size size_;
  float last_requested_scale_;
};

class NullSource: public ImageSkiaSource {
 public:
  NullSource() {
  }

  NullSource(const NullSource&) = delete;
  NullSource& operator=(const NullSource&) = delete;

  ~NullSource() override {}

  ImageSkiaRep GetImageForScale(float scale) override {
    return gfx::ImageSkiaRep();
  }
};

}  // namespace

namespace test {
class TestOnThread : public base::SimpleThread {
 public:
  explicit TestOnThread(ImageSkia* image_skia)
      : SimpleThread("image_skia_on_thread"),
        image_skia_(image_skia),
        can_read_(false),
        can_modify_(false) {
  }

  TestOnThread(const TestOnThread&) = delete;
  TestOnThread& operator=(const TestOnThread&) = delete;

  void Run() override {
    can_read_ = image_skia_->CanRead();
    can_modify_ = image_skia_->CanModify();
    if (can_read_)
      image_skia_->image_reps();
  }

  void StartAndJoin() {
    Start();
    Join();
  }

  bool can_read() const { return can_read_; }

  bool can_modify() const { return can_modify_; }

 private:
  raw_ptr<ImageSkia> image_skia_;

  bool can_read_;
  bool can_modify_;
};

}  // namespace test

class ImageSkiaTest : public testing::Test {
 public:
  ImageSkiaTest() = default;
  ImageSkiaTest(const ImageSkiaTest&) = delete;
  ImageSkiaTest& operator=(const ImageSkiaTest&) = delete;
  ~ImageSkiaTest() override = default;

 private:
  ui::test::ScopedSetSupportedResourceScaleFactors
      scoped_set_supported_scale_factors_{{ui::k100Percent, ui::k200Percent}};
};

TEST_F(ImageSkiaTest, FixedSource) {
  ImageSkiaRep image(Size(100, 200), 0.0f);
  ImageSkia image_skia(std::make_unique<FixedSource>(image), Size(100, 200));
  EXPECT_EQ(0U, image_skia.image_reps().size());

  const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f);
  EXPECT_EQ(100, result_100p.GetWidth());
  EXPECT_EQ(200, result_100p.GetHeight());
  EXPECT_EQ(1.0f, result_100p.scale());
  EXPECT_EQ(1U, image_skia.image_reps().size());

  const ImageSkiaRep& result_200p = image_skia.GetRepresentation(2.0f);

  EXPECT_EQ(100, result_200p.GetWidth());
  EXPECT_EQ(200, result_200p.GetHeight());
  EXPECT_EQ(100, result_200p.pixel_width());
  EXPECT_EQ(200, result_200p.pixel_height());
  EXPECT_EQ(1.0f, result_200p.scale());
  EXPECT_EQ(1U, image_skia.image_reps().size());

  const ImageSkiaRep& result_300p = image_skia.GetRepresentation(3.0f);

  EXPECT_EQ(100, result_300p.GetWidth());
  EXPECT_EQ(200, result_300p.GetHeight());
  EXPECT_EQ(100, result_300p.pixel_width());
  EXPECT_EQ(200, result_300p.pixel_height());
  EXPECT_EQ(1.0f, result_300p.scale());
  EXPECT_EQ(1U, image_skia.image_reps().size());

  // Get the representation again and make sure it doesn't
  // generate new image skia rep.
  image_skia.GetRepresentation(1.0f);
  image_skia.GetRepresentation(2.0f);
  image_skia.GetRepresentation(3.0f);
  EXPECT_EQ(1U, image_skia.image_reps().size());
}

TEST_F(ImageSkiaTest, FixedScaledSource) {
  ImageSkiaRep image(Size(100, 200), 1.0f);
  ImageSkia image_skia(std::make_unique<FixedScaleSource>(image),
                       Size(100, 200));
  EXPECT_EQ(0U, image_skia.image_reps().size());

  const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f);
  EXPECT_EQ(100, result_100p.GetWidth());
  EXPECT_EQ(200, result_100p.GetHeight());
  EXPECT_EQ(1.0f, result_100p.scale());
  EXPECT_EQ(1U, image_skia.image_reps().size());

  // 2.0f data doesn't exist, then it falls back to 1.0f and rescale it.
  const ImageSkiaRep& result_200p = image_skia.GetRepresentation(2.0f);

  EXPECT_EQ(100, result_200p.GetWidth());
  EXPECT_EQ(200, result_200p.GetHeight());
  EXPECT_EQ(200, result_200p.pixel_width());
  EXPECT_EQ(400, result_200p.pixel_height());
  EXPECT_EQ(2.0f, result_200p.scale());
  EXPECT_EQ(2U, image_skia.image_reps().size());

  // Get the representation again and make sure it doesn't
  // generate new image skia rep.
  image_skia.GetRepresentation(1.0f);
  image_skia.GetRepresentation(2.0f);
  EXPECT_EQ(2U, image_skia.image_reps().size());
}

TEST_F(ImageSkiaTest, FixedUnscaledSource) {
  ImageSkiaRep image(Size(100, 200), 0.0f);
  ImageSkia image_skia(std::make_unique<FixedScaleSource>(image),
                       Size(100, 200));
  EXPECT_EQ(0U, image_skia.image_reps().size());

  const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f);
  EXPECT_EQ(100, result_100p.pixel_width());
  EXPECT_EQ(200, result_100p.pixel_height());
  EXPECT_TRUE(result_100p.unscaled());
  EXPECT_EQ(1U, image_skia.image_reps().size());

  // 2.0f data doesn't exist, but unscaled ImageSkiaRep shouldn't be rescaled.
  const ImageSkiaRep& result_200p = image_skia.GetRepresentation(2.0f);

  EXPECT_EQ(100, result_200p.pixel_width());
  EXPECT_EQ(200, result_200p.pixel_height());
  EXPECT_TRUE(result_200p.unscaled());
  EXPECT_EQ(1U, image_skia.image_reps().size());

  // Get the representation again and make sure it doesn't
  // generate new image skia rep.
  image_skia.GetRepresentation(1.0f);
  image_skia.GetRepresentation(2.0f);
  EXPECT_EQ(1U, image_skia.image_reps().size());
}

TEST_F(ImageSkiaTest, DynamicSource) {
  ImageSkia image_skia(std::make_unique<DynamicSource>(Size(100, 200)),
                       Size(100, 200));
  EXPECT_EQ(0U, image_skia.image_reps().size());
  const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f);
  EXPECT_EQ(100, result_100p.GetWidth());
  EXPECT_EQ(200, result_100p.GetHeight());
  EXPECT_EQ(1.0f, result_100p.scale());
  EXPECT_EQ(1U, image_skia.image_reps().size());

  const ImageSkiaRep& result_200p =
      image_skia.GetRepresentation(2.0f);
  EXPECT_EQ(100, result_200p.GetWidth());
  EXPECT_EQ(200, result_200p.GetHeight());
  EXPECT_EQ(200, result_200p.pixel_width());
  EXPECT_EQ(400, result_200p.pixel_height());
  EXPECT_EQ(2.0f, result_200p.scale());
  EXPECT_EQ(2U, image_skia.image_reps().size());

  // Get the representation again and make sure it doesn't
  // generate new image skia rep.
  image_skia.GetRepresentation(1.0f);
  EXPECT_EQ(2U, image_skia.image_reps().size());
  image_skia.GetRepresentation(2.0f);
  EXPECT_EQ(2U, image_skia.image_reps().size());
}

// Tests that image_reps returns all of the representations in the
// image when there are multiple representations for a scale factor.
// This currently is the case with ImageLoader::LoadImages.
TEST_F(ImageSkiaTest, ManyRepsPerScaleFactor) {
  const int kSmallIcon1x = 16;
  const int kSmallIcon2x = 32;
  const int kLargeIcon1x = 32;

  ImageSkia image(std::make_unique<NullSource>(),
                  gfx::Size(kSmallIcon1x, kSmallIcon1x));
  // Simulate a source which loads images on a delay. Upon
  // GetImageForScaleFactor, it immediately returns null and starts loading
  // image reps slowly.
  image.GetRepresentation(1.0f);
  image.GetRepresentation(2.0f);

  // After a lengthy amount of simulated time, finally loaded image reps.
  image.AddRepresentation(ImageSkiaRep(
      gfx::Size(kSmallIcon1x, kSmallIcon1x), 1.0f));
  image.AddRepresentation(ImageSkiaRep(
      gfx::Size(kSmallIcon2x, kSmallIcon2x), 2.0f));
  image.AddRepresentation(ImageSkiaRep(
      gfx::Size(kLargeIcon1x, kLargeIcon1x), 1.0f));

  std::vector<ImageSkiaRep> image_reps = image.image_reps();
  EXPECT_EQ(3u, image_reps.size());

  int num_1x = 0;
  int num_2x = 0;
  for (size_t i = 0; i < image_reps.size(); ++i) {
    if (image_reps[i].scale() == 1.0f)
      num_1x++;
    else if (image_reps[i].scale() == 2.0f)
      num_2x++;
  }
  EXPECT_EQ(2, num_1x);
  EXPECT_EQ(1, num_2x);
}

TEST_F(ImageSkiaTest, GetBitmap) {
  ImageSkia image_skia(std::make_unique<DynamicSource>(Size(100, 200)),
                       Size(100, 200));
  const SkBitmap* bitmap = image_skia.bitmap();
  ASSERT_NE(nullptr, bitmap);
  EXPECT_FALSE(bitmap->isNull());
}

TEST_F(ImageSkiaTest, GetBitmapFromEmpty) {
  // Create an image with 1 representation and remove it so the ImageSkiaStorage
  // is left with no representations.
  ImageSkia empty_image(ImageSkiaRep(Size(100, 200), 1.0f));
  ImageSkia empty_image_copy(empty_image);
  empty_image.RemoveRepresentation(1.0f);

  // Check that ImageSkia::bitmap() still returns a valid SkBitmap pointer for
  // the image and all its copies.
  const SkBitmap* bitmap = empty_image_copy.bitmap();
  ASSERT_NE(nullptr, bitmap);
  EXPECT_TRUE(bitmap->isNull());
  EXPECT_TRUE(bitmap->empty());
}

TEST_F(ImageSkiaTest, BackedBySameObjectAs) {
  // Null images should all be backed by the same object (NULL).
  ImageSkia image;
  ImageSkia unrelated;
  EXPECT_TRUE(image.BackedBySameObjectAs(unrelated));

  image.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(10, 10),
                                            1.0f));
  ImageSkia copy = image;
  copy.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(10, 10),
                                           2.0f));
  unrelated.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(10, 10),
                                                1.0f));
  EXPECT_TRUE(image.BackedBySameObjectAs(copy));
  EXPECT_FALSE(image.BackedBySameObjectAs(unrelated));
  EXPECT_FALSE(copy.BackedBySameObjectAs(unrelated));
}

#if DCHECK_IS_ON()
TEST_F(ImageSkiaTest, EmptyOnThreadTest) {
  ImageSkia empty;
  test::TestOnThread empty_on_thread(&empty);
  empty_on_thread.Start();
  empty_on_thread.Join();
  EXPECT_TRUE(empty_on_thread.can_read());
  EXPECT_TRUE(empty_on_thread.can_modify());
}

TEST_F(ImageSkiaTest, StaticOnThreadTest) {
  ImageSkia image(ImageSkiaRep(Size(100, 200), 1.0f));
  EXPECT_FALSE(image.IsThreadSafe());

  test::TestOnThread image_on_thread(&image);
  // an image that was never accessed on this thread can be
  // read by other thread.
  image_on_thread.StartAndJoin();
  EXPECT_TRUE(image_on_thread.can_read());
  EXPECT_TRUE(image_on_thread.can_modify());
  EXPECT_FALSE(image.CanRead());
  EXPECT_FALSE(image.CanModify());

  image.DetachStorageFromSequence();
  // An image is accessed by this thread,
  // so other thread cannot read/modify it.
  image.image_reps();
  test::TestOnThread image_on_thread2(&image);
  image_on_thread2.StartAndJoin();
  EXPECT_FALSE(image_on_thread2.can_read());
  EXPECT_FALSE(image_on_thread2.can_modify());
  EXPECT_TRUE(image.CanRead());
  EXPECT_TRUE(image.CanModify());

  image.DetachStorageFromSequence();
  ImageSkia deep_copy(image.DeepCopy());
  EXPECT_FALSE(deep_copy.IsThreadSafe());
  test::TestOnThread deepcopy_on_thread(&deep_copy);
  deepcopy_on_thread.StartAndJoin();
  EXPECT_TRUE(deepcopy_on_thread.can_read());
  EXPECT_TRUE(deepcopy_on_thread.can_modify());
  EXPECT_FALSE(deep_copy.CanRead());
  EXPECT_FALSE(deep_copy.CanModify());

  ImageSkia deep_copy2(image.DeepCopy());
  EXPECT_EQ(1U, deep_copy2.image_reps().size());
  // Access it from current thread so that it can't be
  // accessed from another thread.
  deep_copy2.image_reps();
  EXPECT_FALSE(deep_copy2.IsThreadSafe());
  test::TestOnThread deepcopy2_on_thread(&deep_copy2);
  deepcopy2_on_thread.StartAndJoin();
  EXPECT_FALSE(deepcopy2_on_thread.can_read());
  EXPECT_FALSE(deepcopy2_on_thread.can_modify());
  EXPECT_TRUE(deep_copy2.CanRead());
  EXPECT_TRUE(deep_copy2.CanModify());

  image.DetachStorageFromSequence();
  image.SetReadOnly();
  // A read-only ImageSkia with no source is thread safe.
  EXPECT_TRUE(image.IsThreadSafe());
  test::TestOnThread readonly_on_thread(&image);
  readonly_on_thread.StartAndJoin();
  EXPECT_TRUE(readonly_on_thread.can_read());
  EXPECT_FALSE(readonly_on_thread.can_modify());
  EXPECT_TRUE(image.CanRead());
  EXPECT_FALSE(image.CanModify());

  image.DetachStorageFromSequence();
  image.MakeThreadSafe();
  EXPECT_TRUE(image.IsThreadSafe());
  test::TestOnThread threadsafe_on_thread(&image);
  threadsafe_on_thread.StartAndJoin();
  EXPECT_TRUE(threadsafe_on_thread.can_read());
  EXPECT_FALSE(threadsafe_on_thread.can_modify());
  EXPECT_TRUE(image.CanRead());
  EXPECT_FALSE(image.CanModify());
}

TEST_F(ImageSkiaTest, SourceOnThreadTest) {
  ImageSkia image(std::make_unique<DynamicSource>(Size(100, 200)),
                  Size(100, 200));
  EXPECT_FALSE(image.IsThreadSafe());

  test::TestOnThread image_on_thread(&image);
  image_on_thread.StartAndJoin();
  // an image that was never accessed on this thread can be
  // read by other thread.
  EXPECT_TRUE(image_on_thread.can_read());
  EXPECT_TRUE(image_on_thread.can_modify());
  EXPECT_FALSE(image.CanRead());
  EXPECT_FALSE(image.CanModify());

  image.DetachStorageFromSequence();
  // An image is accessed by this thread,
  // so other thread cannot read/modify it.
  image.image_reps();
  test::TestOnThread image_on_thread2(&image);
  image_on_thread2.StartAndJoin();
  EXPECT_FALSE(image_on_thread2.can_read());
  EXPECT_FALSE(image_on_thread2.can_modify());
  EXPECT_TRUE(image.CanRead());
  EXPECT_TRUE(image.CanModify());

  image.DetachStorageFromSequence();
  image.SetReadOnly();
  EXPECT_FALSE(image.IsThreadSafe());
  test::TestOnThread readonly_on_thread(&image);
  readonly_on_thread.StartAndJoin();
  EXPECT_TRUE(readonly_on_thread.can_read());
  EXPECT_FALSE(readonly_on_thread.can_modify());
  EXPECT_FALSE(image.CanRead());
  EXPECT_FALSE(image.CanModify());

  image.DetachStorageFromSequence();
  image.MakeThreadSafe();
  EXPECT_TRUE(image.IsThreadSafe());
  // Check if image reps are generated for supported scale factors.
  EXPECT_EQ(ui::GetSupportedResourceScaleFactors().size(),
            image.image_reps().size());
  test::TestOnThread threadsafe_on_thread(&image);
  threadsafe_on_thread.StartAndJoin();
  EXPECT_TRUE(threadsafe_on_thread.can_read());
  EXPECT_FALSE(threadsafe_on_thread.can_modify());
  EXPECT_TRUE(image.CanRead());
  EXPECT_FALSE(image.CanModify());
}
#endif  // DCHECK_IS_ON()

TEST_F(ImageSkiaTest, Unscaled) {
  SkBitmap bitmap;

  // An ImageSkia created with 1x bitmap is unscaled.
  ImageSkia image_skia = ImageSkia::CreateFrom1xBitmap(bitmap);
  EXPECT_TRUE(image_skia.GetRepresentation(1.0f).unscaled());
  ImageSkiaRep rep_2x(Size(100, 100), 2.0f);

  // When reps for other scales are added, the unscaled image
  // becomes scaled.
  image_skia.AddRepresentation(rep_2x);
  EXPECT_FALSE(image_skia.GetRepresentation(1.0f).unscaled());
  EXPECT_FALSE(image_skia.GetRepresentation(2.0f).unscaled());
}

namespace {

std::vector<float> GetSortedScaleFactors(const gfx::ImageSkia& image) {
  const std::vector<ImageSkiaRep>& image_reps = image.image_reps();
  std::vector<float> scale_factors;
  for (size_t i = 0; i < image_reps.size(); ++i) {
    scale_factors.push_back(image_reps[i].scale());
  }
  std::sort(scale_factors.begin(), scale_factors.end());
  return scale_factors;
}

}  // namespace

TEST_F(ImageSkiaTest, ArbitraryScaleFactor) {
  // source is owned by |image|
  DynamicSource* source = new DynamicSource(Size(100, 200));
  ImageSkia image(base::WrapUnique(source), gfx::Size(100, 200));

  image.GetRepresentation(1.5f);
  EXPECT_EQ(2.0f, source->GetLastRequestedScaleAndReset());
  std::vector<ImageSkiaRep> image_reps = image.image_reps();
  EXPECT_EQ(2u, image_reps.size());

  std::vector<float> scale_factors = GetSortedScaleFactors(image);
  EXPECT_EQ(1.5f, scale_factors[0]);
  EXPECT_EQ(2.0f, scale_factors[1]);

  // Requesting 1.75 scale factor also falls back to 2.0f and rescale.
  // However, the image already has the 2.0f data, so it won't fetch again.
  image.GetRepresentation(1.75f);
  EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset());
  image_reps = image.image_reps();
  EXPECT_EQ(3u, image_reps.size());

  scale_factors = GetSortedScaleFactors(image);
  EXPECT_EQ(1.5f, scale_factors[0]);
  EXPECT_EQ(1.75f, scale_factors[1]);
  EXPECT_EQ(2.0f, scale_factors[2]);

  // Requesting 1.25 scale factor also falls back to 2.0f and rescale.
  // However, the image already has the 2.0f data, so it won't fetch again.
  image.GetRepresentation(1.25f);
  EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset());
  image_reps = image.image_reps();
  EXPECT_EQ(4u, image_reps.size());
  scale_factors = GetSortedScaleFactors(image);
  EXPECT_EQ(1.25f, scale_factors[0]);
  EXPECT_EQ(1.5f, scale_factors[1]);
  EXPECT_EQ(1.75f, scale_factors[2]);
  EXPECT_EQ(2.0f, scale_factors[3]);

  // 1.20 is falled back to 1.0.
  image.GetRepresentation(1.20f);
  EXPECT_EQ(1.0f, source->GetLastRequestedScaleAndReset());
  image_reps = image.image_reps();
  EXPECT_EQ(6u, image_reps.size());
  scale_factors = GetSortedScaleFactors(image);
  EXPECT_EQ(1.0f, scale_factors[0]);
  EXPECT_EQ(1.2f, scale_factors[1]);
  EXPECT_EQ(1.25f, scale_factors[2]);
  EXPECT_EQ(1.5f, scale_factors[3]);
  EXPECT_EQ(1.75f, scale_factors[4]);
  EXPECT_EQ(2.0f, scale_factors[5]);

  // Scale factor less than 1.0f will be falled back to 1.0f
  image.GetRepresentation(0.75f);
  EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset());
  image_reps = image.image_reps();
  EXPECT_EQ(7u, image_reps.size());

  scale_factors = GetSortedScaleFactors(image);
  EXPECT_EQ(0.75f, scale_factors[0]);
  EXPECT_EQ(1.0f, scale_factors[1]);
  EXPECT_EQ(1.2f, scale_factors[2]);
  EXPECT_EQ(1.25f, scale_factors[3]);
  EXPECT_EQ(1.5f, scale_factors[4]);
  EXPECT_EQ(1.75f, scale_factors[5]);
  EXPECT_EQ(2.0f, scale_factors[6]);

  // Scale factor greater than 2.0f is falled back to 2.0f because it's not
  // supported.
  image.GetRepresentation(3.0f);
  EXPECT_EQ(0.0f, source->GetLastRequestedScaleAndReset());
  image_reps = image.image_reps();
  EXPECT_EQ(8u, image_reps.size());
}

TEST_F(ImageSkiaTest, ArbitraryScaleFactorWithMissingResource) {
  ImageSkia image(
      std::make_unique<FixedScaleSource>(ImageSkiaRep(Size(100, 200), 1.0f)),
      Size(100, 200));

  // Requesting 1.5f -- falls back to 2.0f, but couldn't find. It should
  // look up 1.0f and then rescale it. Note that the rescaled ImageSkiaRep will
  // have 2.0f scale.
  const ImageSkiaRep& rep = image.GetRepresentation(1.5f);
  EXPECT_EQ(1.5f, rep.scale());
  EXPECT_EQ(2U, image.image_reps().size());
  EXPECT_EQ(2.0f, image.image_reps()[0].scale());
  EXPECT_EQ(1.5f, image.image_reps()[1].scale());
}

TEST_F(ImageSkiaTest, UnscaledImageForArbitraryScaleFactor) {
  // 0.0f means unscaled.
  ImageSkia image(
      std::make_unique<FixedScaleSource>(ImageSkiaRep(Size(100, 200), 0.0f)),
      Size(100, 200));

  // Requesting 2.0f, which should return 1.0f unscaled image.
  const ImageSkiaRep& rep = image.GetRepresentation(2.0f);
  EXPECT_EQ(1.0f, rep.scale());
  EXPECT_EQ("100x200", rep.pixel_size().ToString());
  EXPECT_TRUE(rep.unscaled());
  EXPECT_EQ(1U, image.image_reps().size());

  // Same for any other scale factors.
  const ImageSkiaRep& rep15 = image.GetRepresentation(1.5f);
  EXPECT_EQ(1.0f, rep15.scale());
  EXPECT_EQ("100x200", rep15.pixel_size().ToString());
  EXPECT_TRUE(rep15.unscaled());
  EXPECT_EQ(1U, image.image_reps().size());

  const ImageSkiaRep& rep12 = image.GetRepresentation(1.2f);
  EXPECT_EQ(1.0f, rep12.scale());
  EXPECT_EQ("100x200", rep12.pixel_size().ToString());
  EXPECT_TRUE(rep12.unscaled());
  EXPECT_EQ(1U, image.image_reps().size());
}

// Tests that searching for representation uses supported scale values and that
// scale values (floats) are not mismatched with scale factors (enums).
TEST_F(ImageSkiaTest, SupportedScaleValue) {
  // ui::kScaleFactorNone isn't probably a good choice but we need a scale
  // factor with the enum value different from its corresponding scale value.
  // The only enum value which meets this requirement is kScaleFactorNone (enum
  // value 0 and scale value 1.0f). Enum values for other scale factors are
  // equal to scale values (for instance enum value for k200Percent is 2 and its
  // scale value is 2.0f).
  ui::test::ScopedSetSupportedResourceScaleFactors
      supported_scale_factors_override(
          {ui::k200Percent, ui::k300Percent, ui::kScaleFactorNone});

  DynamicSource* source = new DynamicSource(Size(100, 200));
  ImageSkia image(base::WrapUnique(source), gfx::Size(100, 200));

  image.GetRepresentation(2.0f);
  EXPECT_EQ(2.0f, source->GetLastRequestedScaleAndReset());

  image.GetRepresentation(3.0f);
  EXPECT_EQ(3.0f, source->GetLastRequestedScaleAndReset());

  // We request 1.0 scale but k100Percent is not currently supported. However,
  // kScaleFactorNone also uses 1.0 scale value. 1.0 is a valid scale then, and
  // we expect that source is asked for this scale. This test case also verifies
  // the fix for regression introduced in the past, where scale factor enum was
  // used in place of scale value.
  image.GetRepresentation(1.0f);
  EXPECT_EQ(1.0f, source->GetLastRequestedScaleAndReset());
}

}  // namespace gfx