| // Copyright 2020 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "pdf/ppapi_migration/graphics.h" | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "base/callback_helpers.h" | 
 | #include "cc/test/pixel_comparator.h" | 
 | #include "cc/test/pixel_test_utils.h" | 
 | #include "pdf/ppapi_migration/bitmap.h" | 
 | #include "pdf/ppapi_migration/callback.h" | 
 | #include "pdf/ppapi_migration/image.h" | 
 | #include "pdf/test/test_helpers.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "third_party/skia/include/core/SkCanvas.h" | 
 | #include "third_party/skia/include/core/SkImage.h" | 
 | #include "third_party/skia/include/core/SkImageInfo.h" | 
 | #include "third_party/skia/include/core/SkRefCnt.h" | 
 | #include "third_party/skia/include/core/SkSize.h" | 
 | #include "third_party/skia/include/core/SkSurface.h" | 
 | #include "ui/gfx/geometry/rect.h" | 
 | #include "ui/gfx/geometry/size.h" | 
 | #include "ui/gfx/skia_util.h" | 
 |  | 
 | namespace chrome_pdf { | 
 |  | 
 | namespace { | 
 |  | 
 | struct FakeSkiaGraphicsClient : public SkiaGraphics::Client { | 
 |   FakeSkiaGraphicsClient() = default; | 
 |   ~FakeSkiaGraphicsClient() override = default; | 
 |  | 
 |   void UpdateSnapshot(sk_sp<SkImage> new_snapshot) override { | 
 |     snapshot = std::move(new_snapshot); | 
 |   } | 
 |  | 
 |   sk_sp<SkImage> snapshot; | 
 | }; | 
 |  | 
 | SkBitmap GenerateExpectedBitmap(const SkISize& graphics_size, | 
 |                                 const SkIRect& rect) { | 
 |   SkBitmap bitmap = CreateN32PremulSkBitmap(graphics_size); | 
 |   bitmap.erase(SK_ColorRED, rect); | 
 |   return bitmap; | 
 | } | 
 |  | 
 | // Creates a nonuniform SkBitmap with given `width` and `height`, such | 
 | // that scrolling will cause a noticeable change to the bitmap. Returns an | 
 | // empty SkBitmap if either `width` or `height` is less than 4. | 
 | SkBitmap CreateNonuniformBitmap(int width, int height) { | 
 |   if (width < 4 || height < 4) | 
 |     return SkBitmap(); | 
 |  | 
 |   SkBitmap bitmap = CreateN32PremulSkBitmap(SkISize::Make(width, height)); | 
 |   bitmap.eraseColor(SK_ColorRED); | 
 |   bitmap.erase(SK_ColorGREEN, {1, 1, width - 1, height - 2}); | 
 |   bitmap.erase(SK_ColorBLACK, {2, 3, 1, 2}); | 
 |   return bitmap; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | class SkiaGraphicsTest : public testing::Test { | 
 |  protected: | 
 |   void TestPaintImageResult(const SkISize& graphics_size, | 
 |                             const gfx::Size& src_size, | 
 |                             const gfx::Rect& paint_rect, | 
 |                             const SkIRect& overlapped_rect) { | 
 |     graphics_ = | 
 |         SkiaGraphics::Create(&client_, gfx::SkISizeToSize(graphics_size)); | 
 |     ASSERT_TRUE(graphics_); | 
 |  | 
 |     // Create snapshots as SkImage and SkBitmap after painting. | 
 |     graphics_->PaintImage(CreateSkiaImageForTesting(src_size, SK_ColorRED), | 
 |                           paint_rect); | 
 |     graphics_->Flush(base::DoNothing()); | 
 |     SkBitmap snapshot_bitmap; | 
 |     ASSERT_TRUE(client_.snapshot->asLegacyBitmap(&snapshot_bitmap)); | 
 |  | 
 |     // Verify snapshot dimensions. | 
 |     EXPECT_EQ(client_.snapshot->dimensions(), graphics_size) | 
 |         << client_.snapshot->width() << " x " << client_.snapshot->height() | 
 |         << " != " << graphics_size.width() << " x " << graphics_size.height(); | 
 |  | 
 |     // Verify the snapshot matches the expected result. | 
 |     const SkBitmap expected_bitmap = | 
 |         GenerateExpectedBitmap(graphics_size, overlapped_rect); | 
 |     EXPECT_TRUE( | 
 |         cc::MatchesBitmap(snapshot_bitmap, expected_bitmap, | 
 |                           cc::ExactPixelComparator(/*discard_alpha=*/false))) | 
 |         << "SkBitmap comparison failed for graphics size of " | 
 |         << graphics_size.width() << " x " << graphics_size.height(); | 
 |   } | 
 |  | 
 |   FakeSkiaGraphicsClient client_; | 
 |  | 
 |   std::unique_ptr<Graphics> graphics_; | 
 | }; | 
 |  | 
 | class SkiaGraphicsScrollTest : public SkiaGraphicsTest { | 
 |  protected: | 
 |   static constexpr gfx::Rect kGraphicsRect = gfx::Rect(4, 5); | 
 |  | 
 |   // Initializes `initial_bitmap_` and `graphics_` before scrolling tests. | 
 |   void SetUp() override { | 
 |     graphics_ = SkiaGraphics::Create(&client_, kGraphicsRect.size()); | 
 |     ASSERT_TRUE(graphics_); | 
 |  | 
 |     // Paint a nonuniform SkBitmap to graphics. | 
 |     initial_bitmap_ = | 
 |         CreateNonuniformBitmap(kGraphicsRect.width(), kGraphicsRect.height()); | 
 |     graphics_->PaintImage(Image(initial_bitmap_), kGraphicsRect); | 
 |     graphics_->Flush(base::DoNothing()); | 
 |     SkBitmap initial_snapshot; | 
 |     ASSERT_TRUE(client_.snapshot->asLegacyBitmap(&initial_snapshot)); | 
 |     ASSERT_TRUE(cc::MatchesBitmap(initial_snapshot, initial_bitmap_, | 
 |                                   cc::ExactPixelComparator(false))); | 
 |   } | 
 |  | 
 |   // Resets the canvas with `initial_bitmap_`, then scrolls it by | 
 |   // `scroll_amount`. | 
 |   void ResetAndScroll(const gfx::Vector2d& scroll_amount) { | 
 |     if (!graphics_) | 
 |       return; | 
 |  | 
 |     graphics_->PaintImage(Image(initial_bitmap_), kGraphicsRect); | 
 |     graphics_->Scroll(kGraphicsRect, scroll_amount); | 
 |     graphics_->Flush(base::DoNothing()); | 
 |   } | 
 |  | 
 |   SkBitmap initial_bitmap_; | 
 | }; | 
 |  | 
 | // static | 
 | constexpr gfx::Rect SkiaGraphicsScrollTest::kGraphicsRect; | 
 |  | 
 | TEST_F(SkiaGraphicsTest, Flush) { | 
 |   graphics_ = SkiaGraphics::Create(&client_, gfx::Size(20, 20)); | 
 |   ASSERT_TRUE(graphics_); | 
 |  | 
 |   // The client's snapshot is nullptr before flushing. | 
 |   EXPECT_FALSE(client_.snapshot); | 
 |  | 
 |   EXPECT_TRUE(graphics_->Flush(base::DoNothing())); | 
 |  | 
 |   // The client's snapshot has changed after flushing. | 
 |   EXPECT_TRUE(client_.snapshot); | 
 | } | 
 |  | 
 | TEST_F(SkiaGraphicsTest, PaintImage) { | 
 |   struct PaintImageParams { | 
 |     // Size of the graphics to be painted on. | 
 |     SkISize graphics_size; | 
 |  | 
 |     // Size of the source image. | 
 |     gfx::Size src_size; | 
 |  | 
 |     // Painting area. | 
 |     gfx::Rect paint_rect; | 
 |  | 
 |     // Common area of the graphics, the source image and the painting area. | 
 |     SkIRect overlapped_rect; | 
 |   }; | 
 |  | 
 |   static constexpr PaintImageParams kPaintImageTestParams[] = { | 
 |       // Paint area is within the graphics and the source image. | 
 |       {{20, 20}, {15, 15}, gfx::Rect(0, 0, 10, 10), {0, 0, 10, 10}}, | 
 |       // Paint area is not completely within the graphics, or the source | 
 |       // image. | 
 |       {{50, 30}, {30, 50}, gfx::Rect(10, 10, 30, 30), {10, 10, 30, 30}}, | 
 |       // Paint area is outside the graphics. | 
 |       {{10, 10}, {30, 30}, gfx::Rect(10, 10, 10, 10), {0, 0, 0, 0}}, | 
 |       // Paint area is outside the source image. | 
 |       {{15, 15}, {5, 5}, gfx::Rect(10, 10, 5, 5), {0, 0, 0, 0}}, | 
 |   }; | 
 |  | 
 |   for (const auto& params : kPaintImageTestParams) | 
 |     TestPaintImageResult(params.graphics_size, params.src_size, | 
 |                          params.paint_rect, params.overlapped_rect); | 
 | } | 
 |  | 
 | TEST_F(SkiaGraphicsScrollTest, InvalidScroll) { | 
 |   static constexpr gfx::Vector2d kNoOpScrollAmounts[] = { | 
 |       // Scroll to the edge of the graphics rect. | 
 |       {kGraphicsRect.width(), 0}, | 
 |       {-kGraphicsRect.width(), 0}, | 
 |       {0, kGraphicsRect.height()}, | 
 |       {0, -kGraphicsRect.height()}, | 
 |       // Scroll outside the graphics rect. | 
 |       {kGraphicsRect.width() + 1, 0}, | 
 |       {-(kGraphicsRect.width() + 2), 0}, | 
 |       {0, kGraphicsRect.height() + 3}, | 
 |       {0, -(kGraphicsRect.height() + 4)}, | 
 |   }; | 
 |  | 
 |   for (const auto& no_op_amount : kNoOpScrollAmounts) { | 
 |     ResetAndScroll(no_op_amount); | 
 |     SkBitmap snapshot; | 
 |     ASSERT_TRUE(client_.snapshot->asLegacyBitmap(&snapshot)); | 
 |     EXPECT_TRUE(cc::MatchesBitmap(snapshot, initial_bitmap_, | 
 |                                   cc::ExactPixelComparator(false))) | 
 |         << "SkBitmap comparison failed for scroll amount of " | 
 |         << no_op_amount.ToString(); | 
 |   } | 
 | } | 
 |  | 
 | TEST_F(SkiaGraphicsScrollTest, Scroll) { | 
 |   static constexpr gfx::Vector2d kValidScrollAmounts[] = { | 
 |       {1, 0}, | 
 |       {-2, 0}, | 
 |       {0, 3}, | 
 |       {0, -3}, | 
 |   }; | 
 |  | 
 |   for (const auto& valid_amount : kValidScrollAmounts) { | 
 |     ResetAndScroll(valid_amount); | 
 |     SkBitmap snapshot; | 
 |     ASSERT_TRUE(client_.snapshot->asLegacyBitmap(&snapshot)); | 
 |     EXPECT_FALSE(cc::MatchesBitmap(snapshot, initial_bitmap_, | 
 |                                    cc::ExactPixelComparator(false))) | 
 |         << "The scroll amount of " << valid_amount.ToString() | 
 |         << " failed to change the snapshot of `graphics_`"; | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace chrome_pdf |