// Copyright 2015 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 "cc/paint/paint_shader.h"

#include "cc/paint/draw_image.h"
#include "cc/paint/image_provider.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/paint/paint_op_buffer.h"
#include "cc/test/fake_paint_image_generator.h"
#include "cc/test/skia_common.h"
#include "cc/test/test_skcanvas.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkSurface.h"

namespace cc {
namespace {

class MockImageGenerator : public FakePaintImageGenerator {
 public:
  explicit MockImageGenerator(const gfx::Size& size)
      : FakePaintImageGenerator(
            SkImageInfo::MakeN32Premul(size.width(), size.height())) {}

  MOCK_METHOD6(GetPixels,
               bool(const SkImageInfo&,
                    void*,
                    size_t,
                    size_t,
                    PaintImage::GeneratorClientId,
                    uint32_t));
};

class MockImageProvider : public ImageProvider {
 public:
  MockImageProvider() = default;
  ~MockImageProvider() override = default;

  ScopedDecodedDrawImage GetDecodedDrawImage(
      const DrawImage& draw_image) override {
    draw_image_ = draw_image;

    SkBitmap bitmap;
    bitmap.allocN32Pixels(10, 10);
    bitmap.eraseColor(SK_ColorBLACK);
    sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
    return ScopedDecodedDrawImage(
        DecodedDrawImage(image, SkSize::MakeEmpty(), SkSize::Make(1.0f, 1.0f),
                         draw_image.filter_quality(), true));
  }

  const DrawImage& draw_image() const { return draw_image_; }

 private:
  DrawImage draw_image_;
};

}  // namespace

TEST(PaintShaderTest, RasterizationRectForRecordShaders) {
  SkMatrix local_matrix = SkMatrix::MakeScale(0.5f, 0.5f);
  auto record_shader = PaintShader::MakePaintRecord(
      sk_make_sp<PaintOpBuffer>(), SkRect::MakeWH(100, 100),
      SkShader::TileMode::kClamp_TileMode, SkShader::TileMode::kClamp_TileMode,
      &local_matrix);

  SkRect tile_rect;
  SkMatrix ctm = SkMatrix::MakeScale(0.5f, 0.5f);
  EXPECT_TRUE(record_shader->GetRasterizationTileRect(ctm, &tile_rect));
  EXPECT_EQ(tile_rect, SkRect::MakeWH(25, 25));
}

TEST(PaintShaderTest, DecodePaintRecord) {
  auto record = sk_make_sp<PaintOpBuffer>();

  // Use a strict mock for the generator. It should never be used when
  // rasterizing this shader, since the decode should be done by the
  // ImageProvider.
  auto generator =
      sk_make_sp<testing::StrictMock<MockImageGenerator>>(gfx::Size(100, 100));
  PaintImage paint_image = PaintImageBuilder::WithDefault()
                               .set_id(PaintImage::GetNextId())
                               .set_paint_image_generator(generator)
                               .TakePaintImage();

  record->push<DrawImageOp>(paint_image, 0.f, 0.f, nullptr);
  SkMatrix local_matrix = SkMatrix::MakeScale(0.5f, 0.5f);
  auto record_shader = PaintShader::MakePaintRecord(
      record, SkRect::MakeWH(100, 100), SkShader::TileMode::kClamp_TileMode,
      SkShader::TileMode::kClamp_TileMode, &local_matrix);
  record_shader->set_has_animated_images(true);

  PaintOpBuffer buffer;
  PaintFlags flags;
  flags.setShader(record_shader);
  buffer.push<ScaleOp>(0.5f, 0.5f);
  buffer.push<DrawRectOp>(SkRect::MakeWH(100, 100), flags);

  MockImageProvider image_provider;
  SaveCountingCanvas canvas;
  buffer.Playback(&canvas, PlaybackParams(&image_provider));

  EXPECT_EQ(canvas.draw_rect_, SkRect::MakeWH(100, 100));
  SkShader* shader = canvas.paint_.getShader();
  ASSERT_TRUE(shader);
  SkMatrix decoded_local_matrix;
  SkShader::TileMode xy[2];
  SkImage* skia_image = shader->isAImage(&decoded_local_matrix, xy);
  ASSERT_TRUE(skia_image);
  EXPECT_TRUE(skia_image->isLazyGenerated());
  EXPECT_EQ(xy[0], record_shader->tx());
  EXPECT_EQ(xy[1], record_shader->ty());
  EXPECT_EQ(decoded_local_matrix, SkMatrix::MakeScale(2.f, 2.f));

  // The rasterization of the shader is internal to skia, so use a raster canvas
  // to verify that the decoded paint does not have the encoded image.
  auto surface = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(100, 100));
  surface->getCanvas()->drawPaint(canvas.paint_);

  // Using the shader requests decode for images at the correct scale.
  EXPECT_EQ(image_provider.draw_image().paint_image(), paint_image);
  EXPECT_EQ(image_provider.draw_image().scale().width(), 0.25f);
  EXPECT_EQ(image_provider.draw_image().scale().height(), 0.25f);
}

}  // namespace cc
