#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 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_;
};
}
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());
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());
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());
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());
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());
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());
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());
}
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));
image.GetRepresentation(1.0f);
image.GetRepresentation(2.0f);
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) {
ImageSkia empty_image(ImageSkiaRep(Size(100, 200), 1.0f));
ImageSkia empty_image_copy(empty_image);
empty_image.RemoveRepresentation(1.0f);
const SkBitmap* bitmap = empty_image_copy.bitmap();
ASSERT_NE(nullptr, bitmap);
EXPECT_TRUE(bitmap->isNull());
EXPECT_TRUE(bitmap->empty());
}
TEST_F(ImageSkiaTest, BackedBySameObjectAs) {
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);
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();
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());
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();
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();
EXPECT_TRUE(image_on_thread.can_read());
EXPECT_TRUE(image_on_thread.can_modify());
EXPECT_FALSE(image.CanRead());
EXPECT_FALSE(image.CanModify());
image.DetachStorageFromSequence();
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());
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
TEST_F(ImageSkiaTest, Unscaled) {
SkBitmap bitmap;
ImageSkia image_skia = ImageSkia::CreateFrom1xBitmap(bitmap);
EXPECT_TRUE(image_skia.GetRepresentation(1.0f).unscaled());
ImageSkiaRep rep_2x(Size(100, 100), 2.0f);
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;
}
}
TEST_F(ImageSkiaTest, ArbitraryScaleFactor) {
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]);
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]);
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]);
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]);
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]);
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));
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) {
ImageSkia image(
std::make_unique<FixedScaleSource>(ImageSkiaRep(Size(100, 200), 0.0f)),
Size(100, 200));
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());
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());
}
TEST_F(ImageSkiaTest, SupportedScaleValue) {
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());
image.GetRepresentation(1.0f);
EXPECT_EQ(1.0f, source->GetLastRequestedScaleAndReset());
}
}