blob: 530b867dff931c7c8769b9be1a0338660374c6ef [file]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/safe_browsing/content/common/visual_utils.h"
#include <array>
#include "base/containers/span.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_discardable_memory_allocator.h"
#include "components/safe_browsing/core/common/features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/private/chromium/SkPMColor.h"
#include "ui/gfx/geometry/rect.h"
namespace safe_browsing::visual_utils {
namespace {
const SkPMColor kSkPMRed = SkPMColorSetARGB(255, 255, 0, 0);
const SkPMColor kSkPMGreen = SkPMColorSetARGB(255, 0, 255, 0);
const SkPMColor kSkPMBlue = SkPMColorSetARGB(255, 0, 0, 255);
} // namespace
using ::testing::FloatEq;
class VisualUtilsTest : public testing::Test {
protected:
void SetUp() override {
base::DiscardableMemoryAllocator::SetInstance(&test_allocator_);
sk_sp<SkColorSpace> rec2020 = SkColorSpace::MakeRGB(
{2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0},
SkNamedGamut::kRec2020);
SkImageInfo bitmap_info = SkImageInfo::MakeN32(
1000, 1000, SkAlphaType::kUnpremul_SkAlphaType, rec2020);
ASSERT_TRUE(bitmap_.tryAllocPixels(bitmap_info));
}
void TearDown() override {
base::DiscardableMemoryAllocator::SetInstance(nullptr);
}
void ExpectBlurredImageOneColor(SkColor expected_color,
const VisualFeatures::BlurredImage& image) {
ExpectBlurredImagePixels(expected_color, image,
gfx::Rect(image.width(), image.height()));
}
void ExpectBlurredImagePixels(SkColor expected_color,
const VisualFeatures::BlurredImage& image,
gfx::Rect area) {
ExpectPixels(expected_color, image.data(), area, image.width());
}
void ExpectPixels(SkColor expected_color,
const std::string& data_to_check,
gfx::Rect area,
int width) {
ASSERT_LE(area.right(), width);
size_t bottom_right_index =
((area.bottom() - 1) * width + area.right() - 1) * 3;
ASSERT_LT(bottom_right_index, data_to_check.size());
for (int x = area.x(); x < area.right(); ++x) {
for (int y = area.y(); y < area.bottom(); ++y) {
size_t index = (y * width + x) * 3;
ExpectPixel(expected_color, data_to_check, x, y, index);
}
}
}
void ExpectPixel(SkColor expected_color,
const std::string& data_to_check,
size_t x,
size_t y,
size_t index) {
ASSERT_LT(index + 2, data_to_check.size());
EXPECT_EQ(SkColorGetR(expected_color),
static_cast<unsigned char>(data_to_check[index]))
<< "R component of pixel at x " << x << " and y " << y
<< " is incorrect.";
EXPECT_EQ(SkColorGetG(expected_color),
static_cast<unsigned char>(data_to_check[index + 1]))
<< "G component of pixel at x " << x << " and y " << y
<< " is incorrect.";
EXPECT_EQ(SkColorGetB(expected_color),
static_cast<unsigned char>(data_to_check[index + 2]))
<< "B component of pixel at x " << x << " and y " << y
<< " is incorrect.";
}
// A test bitmap to work with. Initialized to be 1000x1000 in the Rec 2020
// color space.
SkBitmap bitmap_;
private:
// A DiscardableMemoryAllocator is needed for certain Skia operations.
base::TestDiscardableMemoryAllocator test_allocator_;
};
TEST_F(VisualUtilsTest, BlurImageWhite) {
VisualFeatures::BlurredImage blurred;
// Draw white over the image
bitmap_.erase(SK_ColorWHITE, SkIRect::MakeXYWH(0, 0, 1000, 1000));
ASSERT_TRUE(GetBlurredImage(bitmap_, &blurred));
#if BUILDFLAG(IS_ANDROID)
const int kExpectedBlurredWidth = 18;
const int kExpectedBlurredHeight = 32;
#else
const int kExpectedBlurredWidth = 48;
const int kExpectedBlurredHeight = 48;
#endif
ASSERT_EQ(kExpectedBlurredWidth, blurred.width());
ASSERT_EQ(kExpectedBlurredHeight, blurred.height());
ASSERT_EQ(3u * kExpectedBlurredWidth * kExpectedBlurredHeight,
blurred.data().size());
ExpectBlurredImageOneColor(SK_ColorWHITE, blurred);
}
TEST_F(VisualUtilsTest, BlurImageRed) {
VisualFeatures::BlurredImage blurred;
// Draw red over the image.
for (int x = 0; x < 1000; x++)
for (int y = 0; y < 1000; y++)
*bitmap_.getAddr32(x, y) = kSkPMRed;
ASSERT_TRUE(GetBlurredImage(bitmap_, &blurred));
#if BUILDFLAG(IS_ANDROID)
const int kExpectedBlurredWidth = 18;
const int kExpectedBlurredHeight = 32;
#else
const int kExpectedBlurredWidth = 48;
const int kExpectedBlurredHeight = 48;
#endif
ASSERT_EQ(kExpectedBlurredWidth, blurred.width());
ASSERT_EQ(kExpectedBlurredHeight, blurred.height());
ASSERT_EQ(3u * kExpectedBlurredWidth * kExpectedBlurredHeight,
blurred.data().size());
ExpectBlurredImageOneColor(SK_ColorRED, blurred);
}
TEST_F(VisualUtilsTest, BlurImageHalfWhiteHalfBlack) {
VisualFeatures::BlurredImage blurred;
// Draw black over half the image.
bitmap_.erase(SK_ColorBLACK, SkIRect::MakeXYWH(0, 0, 1000, 500));
// Draw white over half the image
bitmap_.erase(SK_ColorWHITE, SkIRect::MakeXYWH(0, 500, 1000, 1000));
ASSERT_TRUE(GetBlurredImage(bitmap_, &blurred));
#if BUILDFLAG(IS_ANDROID)
ASSERT_EQ(18, blurred.width());
ASSERT_EQ(32, blurred.height());
ASSERT_EQ(3u * 18u * 32u, blurred.data().size());
// The middle blocks may have been blurred to something between white and
// black, so only verify the first 14 and last 14 rows.
ExpectBlurredImagePixels(SK_ColorBLACK, blurred, gfx::Rect(18, 14));
ExpectBlurredImagePixels(SK_ColorWHITE, blurred, gfx::Rect(0, 18, 18, 14));
#else
ASSERT_EQ(48, blurred.width());
ASSERT_EQ(48, blurred.height());
ASSERT_EQ(3u * 48u * 48u, blurred.data().size());
// The middle blocks may have been blurred to something between white and
// black, so only verify the first 22 and last 22 rows.
ExpectBlurredImagePixels(SK_ColorBLACK, blurred, gfx::Rect(48, 22));
ExpectBlurredImagePixels(SK_ColorWHITE, blurred, gfx::Rect(0, 26, 48, 22));
#endif
}
TEST_F(VisualUtilsTest, BlockMeanAverageOneBlock) {
// Draw black over half the image.
bitmap_.erase(SK_ColorBLACK, SkIRect::MakeXYWH(0, 0, 1000, 500));
// Draw white over half the image
bitmap_.erase(SK_ColorWHITE, SkIRect::MakeXYWH(0, 500, 1000, 1000));
std::unique_ptr<SkBitmap> blocks = BlockMeanAverage(bitmap_, 1000);
ASSERT_EQ(1, blocks->width());
ASSERT_EQ(1, blocks->height());
EXPECT_EQ(blocks->getColor(0, 0), SkColorSetRGB(127, 127, 127));
}
TEST_F(VisualUtilsTest, BlockMeanAveragePartialBlocks) {
// Draw a white, red, green, and blue box with the expected block sizes.
bitmap_.erase(SK_ColorWHITE, SkIRect::MakeXYWH(0, 0, 600, 600));
for (int x = 600; x < 1000; x++)
for (int y = 0; y < 600; y++)
*bitmap_.getAddr32(x, y) = kSkPMRed;
for (int x = 0; x < 600; x++)
for (int y = 600; y < 1000; y++)
*bitmap_.getAddr32(x, y) = kSkPMGreen;
for (int x = 600; x < 1000; x++)
for (int y = 600; y < 1000; y++)
*bitmap_.getAddr32(x, y) = kSkPMBlue;
std::unique_ptr<SkBitmap> blocks = BlockMeanAverage(bitmap_, 600);
ASSERT_EQ(2, blocks->width());
ASSERT_EQ(2, blocks->height());
EXPECT_EQ(blocks->getColor(0, 0), SK_ColorWHITE);
EXPECT_EQ(*blocks->getAddr32(1, 0), kSkPMRed);
EXPECT_EQ(*blocks->getAddr32(0, 1), kSkPMGreen);
EXPECT_EQ(*blocks->getAddr32(1, 1), kSkPMBlue);
}
TEST_F(VisualUtilsTest, NonSquareBlurredImage) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
kVisualFeaturesSizes, {{"phash_width", "108"}, {"phash_height", "192"}});
VisualFeatures::BlurredImage blurred;
// Draw white over the image
bitmap_.erase(SK_ColorWHITE, SkIRect::MakeXYWH(0, 0, 1000, 1000));
ASSERT_TRUE(GetBlurredImage(bitmap_, &blurred));
ASSERT_EQ(18, blurred.width());
ASSERT_EQ(32, blurred.height());
ASSERT_EQ(3u * 18u * 32u, blurred.data().size());
for (size_t i = 0; i < 18u * 32u; i++) {
EXPECT_EQ('\xff', blurred.data()[3 * i]);
EXPECT_EQ('\xff', blurred.data()[3 * i + 1]);
EXPECT_EQ('\xff', blurred.data()[3 * i + 2]);
}
}
TEST_F(VisualUtilsTest, EncodeScreenshot) {
const int kBitmapWidth = 40;
const int kBitmapHeight = 40;
SkBitmap bitmap;
bitmap.allocN32Pixels(kBitmapWidth, kBitmapHeight);
bitmap.eraseColor(SK_ColorWHITE);
bitmap.erase(SK_ColorRED, SkIRect::MakeXYWH(0, 0, 20, 10));
bitmap.erase(SK_ColorBLUE, SkIRect::MakeXYWH(20, 0, 20, 10));
VisualFeatures::Screenshot encoded_screenshot;
EncodeScreenshot(bitmap, &encoded_screenshot);
EXPECT_EQ(kBitmapWidth, encoded_screenshot.width());
EXPECT_EQ(kBitmapHeight, encoded_screenshot.height());
ExpectPixels(SK_ColorRED, encoded_screenshot.data(), gfx::Rect(20, 10),
kBitmapWidth);
ExpectPixels(SK_ColorBLUE, encoded_screenshot.data(),
gfx::Rect(20, 0, 20, 10), kBitmapWidth);
ExpectPixels(SK_ColorWHITE, encoded_screenshot.data(),
gfx::Rect(0, 10, 40, 30), kBitmapWidth);
}
} // namespace safe_browsing::visual_utils