text: /*
   * Copyright 2015 Google Inc.
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #include "gm/gm.h"
  #include "include/core/SkBitmap.h"
  #include "include/core/SkBlendMode.h"
  #include "include/core/SkCanvas.h"
  #include "include/core/SkColor.h"
  #include "include/core/SkImage.h"
  #include "include/core/SkImageFilter.h"
  #include "include/core/SkPaint.h"
  #include "include/core/SkPoint.h"
  #include "include/core/SkRect.h"
  #include "include/core/SkRefCnt.h"
  #include "include/core/SkScalar.h"
  #include "include/core/SkShader.h"
  #include "include/core/SkSize.h"
  #include "include/core/SkString.h"
  #include "include/core/SkSurface.h"
  #include "include/core/SkTileMode.h"
  #include "include/core/SkTypes.h"
  #include "include/effects/SkGradientShader.h"
  #include "include/effects/SkImageFilters.h"
  #include "tools/Resources.h"
  #include "tools/ToolUtils.h"
  #include "tools/timer/TimeUtils.h"
  
  #include <utility>
  
  namespace skiagm {
  
  // This GM draws image filters with a CTM containing shearing / rotation.
  // It checks that the scale portion of the CTM is correctly extracted
  // and applied to the image inputs separately from the non-scale portion.
  
  static sk_sp<SkImage> make_gradient_circle(int width, int height) {
      SkScalar x = SkIntToScalar(width / 2);
      SkScalar y = SkIntToScalar(height / 2);
      SkScalar radius = std::min(x, y) * 0.8f;
  
      auto surface(SkSurface::MakeRasterN32Premul(width, height));
      SkCanvas* canvas = surface->getCanvas();
  
      canvas->clear(0x00000000);
      SkColor colors[2];
      colors[0] = SK_ColorWHITE;
      colors[1] = SK_ColorBLACK;
      SkPaint paint;
      paint.setShader(SkGradientShader::MakeRadial(SkPoint::Make(x, y), radius, colors, nullptr, 2,
                                                   SkTileMode::kClamp));
      canvas->drawCircle(x, y, radius, paint);
  
      return surface->makeImageSnapshot();
  }
  
  class ImageFiltersTransformedGM : public GM {
  public:
      ImageFiltersTransformedGM() {
          this->setBGColor(SK_ColorBLACK);
      }
  
  protected:
  
      SkString onShortName() override { return SkString("imagefilterstransformed"); }
  
      SkISize onISize() override { return SkISize::Make(420, 240); }
  
      void onOnceBeforeDraw() override {
          fCheckerboard = SkImage::MakeFromBitmap(
                  ToolUtils::create_checkerboard_bitmap(64, 64, 0xFFA0A0A0, 0xFF404040, 8));
          fGradientCircle = make_gradient_circle(64, 64);
      }
  
      void onDraw(SkCanvas* canvas) override {
          sk_sp<SkImageFilter> gradient(SkImageFilters::Image(fGradientCircle));
          sk_sp<SkImageFilter> checkerboard(SkImageFilters::Image(fCheckerboard));
          sk_sp<SkImageFilter> filters[] = {
              SkImageFilters::Blur(12, 0, nullptr),
              SkImageFilters::DropShadow(0, 15, 8, 0, SK_ColorGREEN, nullptr),
              SkImageFilters::DisplacementMap(SkColorChannel::kR, SkColorChannel::kR, 12,
                                              std::move(gradient), checkerboard),
              SkImageFilters::Dilate(2, 2, checkerboard),
              SkImageFilters::Erode(2, 2, checkerboard),
          };
  
          const SkScalar margin = SkIntToScalar(20);
          const SkScalar size = SkIntToScalar(60);
  
          for (size_t j = 0; j < 3; j++) {
              canvas->save();
              canvas->translate(margin, 0);
              for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
                  SkPaint paint;
                  paint.setColor(SK_ColorWHITE);
                  paint.setImageFilter(filters[i]);
                  paint.setAntiAlias(true);
                  canvas->save();
                  canvas->translate(size * SK_ScalarHalf, size * SK_ScalarHalf);
                  canvas->scale(SkDoubleToScalar(0.8), SkDoubleToScalar(0.8));
                  if (j == 1) {
                      canvas->rotate(SkIntToScalar(45));
                  } else if (j == 2) {
                      canvas->skew(SkDoubleToScalar(0.5), SkDoubleToScalar(0.2));
                  }
                  canvas->translate(-size * SK_ScalarHalf, -size * SK_ScalarHalf);
                  canvas->drawOval(SkRect::MakeXYWH(0, size * SkDoubleToScalar(0.1),
                                                    size, size * SkDoubleToScalar(0.6)), paint);
                  canvas->restore();
                  canvas->translate(size + margin, 0);
              }
              canvas->restore();
              canvas->translate(0, size + margin);
          }
      }
  
  private:
      sk_sp<SkImage> fCheckerboard;
      sk_sp<SkImage> fGradientCircle;
      using INHERITED = GM;
  };
  DEF_GM( return new ImageFiltersTransformedGM; )
  }  // namespace skiagm
  
  //////////////////////////////////////////////////////////////////////////////
  
  DEF_SIMPLE_GM(rotate_imagefilter, canvas, 500, 500) {
      SkPaint paint;
  
      const SkRect r = SkRect::MakeXYWH(50, 50, 100, 100);
  
      sk_sp<SkImageFilter> filters[] = {
          nullptr,
          SkImageFilters::Blur(6, 0, nullptr),
          SkImageFilters::Xfermode(SkBlendMode::kSrcOver, nullptr),
      };
  
      for (auto& filter : filters) {
          paint.setAntiAlias(false);
          paint.setImageFilter(filter);
  
          canvas->save();
  
          canvas->drawRect(r, paint);
  
          canvas->translate(150, 0);
          canvas->save();
              canvas->rotate(30, 100, 100);
              canvas->drawRect(r, paint);
          canvas->restore();
  
          paint.setAntiAlias(true);
          canvas->translate(150, 0);
          canvas->save();
              canvas->rotate(30, 100, 100);
              canvas->drawRect(r, paint);
          canvas->restore();
  
          canvas->restore();
          canvas->translate(0, 150);
      }
  }
  
  class ImageFilterMatrixWLocalMatrix : public skiagm::GM {
  public:
  
      // Start at 132 degrees, since that resulted in a skipped draw before the fix to
      // SkLocalMatrixImageFilter's computeFastBounds() function.
      ImageFilterMatrixWLocalMatrix() : fDegrees(132.f) {}
  
  protected:
      SkString onShortName() override {
          return SkString("imagefilter_matrix_localmatrix");
      }
  
      SkISize onISize() override {
          return SkISize::Make(512, 512);
      }
  
      bool onAnimate(double nanos) override {
          // Animate the rotation angle to ensure the local matrix bounds modifications work
          // for a variety of transformations.
          fDegrees = TimeUtils::Scaled(1e-9f * nanos, 360.f);
          return true;
      }
  
      void onOnceBeforeDraw() override {
          fImage = GetResourceAsImage("images/mandrill_256.png");
      }
  
      void onDraw(SkCanvas* canvas) override {
          SkMatrix localMatrix;
          localMatrix.preTranslate(128, 128);
          localMatrix.preScale(2.0f, 2.0f);
  
          // This matrix applies a rotate around the center of the image (prior to the simulated
          // hi-dpi 2x device scale).
          SkMatrix filterMatrix;
          filterMatrix.setRotate(fDegrees, 64, 64);
  
          sk_sp<SkImageFilter> filter =
                  SkImageFilter::MakeMatrixFilter(filterMatrix, kLow_SkFilterQuality, nullptr)
                               ->makeWithLocalMatrix(localMatrix);
  
          SkPaint p;
          p.setImageFilter(filter);
          canvas->drawImage(fImage.get(), 128, 128, &p);
      }
  
  private:
      SkScalar fDegrees;
      sk_sp<SkImage> fImage;
  };
  
  DEF_GM(return new ImageFilterMatrixWLocalMatrix();)
  