#include "ui/views/test/view_skia_gold_pixel_diff.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkImage.h"
#include "ui/base/test/skia_gold_matching_algorithm.h"
#include "ui/base/test/skia_gold_pixel_diff.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/image/image.h"
#include "ui/snapshot/snapshot.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#if defined(USE_AURA)
#include "ui/snapshot/snapshot_aura.h"
#endif
namespace views {
namespace {
void SnapshotCallback(base::RunLoop* run_loop,
gfx::Image* ret_image,
gfx::Image image) {
*ret_image = image;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, run_loop->QuitClosure());
}
}
ViewSkiaGoldPixelDiff::ViewSkiaGoldPixelDiff(
const std::string& screenshot_prefix,
const std::optional<std::string>& corpus)
: screenshot_prefix_(screenshot_prefix),
pixel_diff_(ui::test::SkiaGoldPixelDiff::GetSession(corpus)) {
CHECK(pixel_diff_);
}
ViewSkiaGoldPixelDiff::~ViewSkiaGoldPixelDiff() = default;
bool ViewSkiaGoldPixelDiff::CompareViewScreenshot(
const std::string& screenshot_name,
const views::View* view,
const ui::test::SkiaGoldMatchingAlgorithm* algorithm) const {
gfx::Rect rc = view->GetBoundsInScreen();
const views::Widget* widget = view->GetWidget();
gfx::Rect bounds_in_screen = widget->GetRootView()->GetBoundsInScreen();
gfx::Rect bounds = widget->GetRootView()->bounds();
rc.Offset(bounds.x() - bounds_in_screen.x(),
bounds.y() - bounds_in_screen.y());
return CompareNativeWindowScreenshot(
screenshot_name, widget->GetNativeWindow(), rc, algorithm);
}
bool ViewSkiaGoldPixelDiff::CompareNativeWindowScreenshot(
const std::string& screenshot_name,
gfx::NativeWindow window,
const gfx::Rect& snapshot_bounds,
const ui::test::SkiaGoldMatchingAlgorithm* algorithm) const {
gfx::Image image;
bool ret = GrabWindowSnapshotInternal(window, snapshot_bounds, &image);
if (!ret) {
return false;
}
return pixel_diff_->CompareScreenshot(
ui::test::SkiaGoldPixelDiff::GetGoldenImageName(
screenshot_prefix_, screenshot_name,
ui::test::SkiaGoldPixelDiff::GetPlatform()),
*image.ToSkBitmap(), algorithm);
}
bool ViewSkiaGoldPixelDiff::CompareNativeWindowScreenshotInRects(
const std::string& screenshot_name,
gfx::NativeWindow window,
const gfx::Rect& snapshot_bounds,
const ui::test::SkiaGoldMatchingAlgorithm* algorithm,
const std::vector<gfx::Rect>& regions_of_interest) const {
CHECK(!algorithm || algorithm->GetCommandLineSwitchName() != "sobel");
gfx::Image image;
bool ret = GrabWindowSnapshotInternal(window, snapshot_bounds, &image);
if (!ret) {
return false;
}
KeepPixelsInRects(regions_of_interest, &image);
return pixel_diff_->CompareScreenshot(
ui::test::SkiaGoldPixelDiff::GetGoldenImageName(
screenshot_prefix_, screenshot_name,
ui::test::SkiaGoldPixelDiff::GetPlatform()),
*image.ToSkBitmap(), algorithm);
}
bool ViewSkiaGoldPixelDiff::GrabWindowSnapshotInternal(
gfx::NativeWindow window,
const gfx::Rect& snapshot_bounds,
gfx::Image* image) const {
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
#if defined(USE_AURA)
ui::GrabWindowSnapshotAura(
#else
ui::GrabWindowSnapshot(
#endif
window, snapshot_bounds,
base::BindOnce(&SnapshotCallback, &run_loop, image));
run_loop.Run();
const bool success = !image->IsEmpty();
if (!success) {
LOG(ERROR) << "Grab screenshot failed.";
}
return success;
}
void ViewSkiaGoldPixelDiff::KeepPixelsInRects(
const std::vector<gfx::Rect>& rects,
gfx::Image* image) const {
CHECK(!rects.empty());
SkBitmap bitmap;
bitmap.allocPixels(image->ToSkBitmap()->info());
bitmap.eraseColor(SK_ColorTRANSPARENT);
SkCanvas canvas(bitmap, SkSurfaceProps{});
SkPaint paint;
for (const auto& rect : rects) {
canvas.drawImageRect(image->ToSkBitmap()->asImage(),
gfx::RectToSkRect(rect), gfx::RectToSkRect(rect),
SkSamplingOptions(), &paint,
SkCanvas::kStrict_SrcRectConstraint);
}
*image = gfx::Image::CreateFrom1xBitmap(bitmap);
}
}