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/SkCanvas.h"
  #include "include/core/SkColor.h"
  #include "include/core/SkFilterQuality.h"
  #include "include/core/SkImage.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/SkSize.h"
  #include "include/core/SkString.h"
  #include "include/core/SkSurface.h"
  #include "include/core/SkTypes.h"
  #include "include/private/SkTArray.h"
  
  class ImageScaleAlignedGM : public skiagm::GM {
  protected:
      void onOnceBeforeDraw() override {
          const SkVector vectors[] = { { 1, 0 }, { 0, 1 } };
  
          for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
             auto& set = fSets.push_back();
  
             set.fVector = vectors[i];
             set.fImages.push_back() = MakeImage(vectors[i], SK_ColorGREEN);
             set.fScales.push_back() = 1;
             set.fImages.push_back() = MakeImage(vectors[i], SK_ColorRED);
             set.fScales.push_back() = kStretchFactor;
             set.fImages.push_back() = MakeImage(vectors[i], SK_ColorGREEN);
             set.fScales.push_back() = 1;
          }
      }
  
      SkString onShortName() override {
          return SkString("image_scale_aligned");
      }
  
      SkISize onISize() override {
          return SkISize::Make(580, 780);
      }
  
      void onDraw(SkCanvas* canvas) override {
          struct {
              SkPoint offset;
              SkVector scale;
          } cfgs[] = {
              {{  10,    10    }, { 1, 1 }},
              {{ 300.5f, 10    }, { 1, 1 }},
              {{  10,    200.5f }, { 1, 1 }},
              {{ 300.5f, 200.5f }, { 1, 1 }},
  
              {{  10.5f, 400.5f }, {  1,  1 }},
              {{ 550.5f, 400.5f }, { -1,  1 }},
              {{  10.5f, 750.5f }, {  1, -1 }},
              {{ 550.5f, 750.5f }, { -1, -1 }},
          };
  
          for (size_t i = 0; i < SK_ARRAY_COUNT(cfgs); ++i) {
              SkAutoCanvasRestore acr(canvas, true);
              canvas->translate(cfgs[i].offset.x(), cfgs[i].offset.y());
              canvas->scale(cfgs[i].scale.x(), cfgs[i].scale.y());
              drawSets(canvas);
          }
      }
  
  private:
      struct ImageSet {
          SkSTArray<3, sk_sp<SkImage>, true>  fImages;
          SkSTArray<3, SkScalar>              fScales;
          SkVector                            fVector;
      };
  
      static sk_sp<SkImage> MakeImage(const SkVector& vec, SkColor color) {
          const SkPoint start = SkPoint::Make(vec.y() * kSegLen / 2, vec.x() * kSegLen / 2);
          const SkPoint end   = SkPoint::Make(start.x() + vec.x() * (kSegLen - 1),
                                              start.y() + vec.y() * (kSegLen - 1));
  
          auto surface(SkSurface::MakeRasterN32Premul(kSegLen, kSegLen));
          surface->getCanvas()->clear(SK_ColorTRANSPARENT);
  
          SkPaint paint;
          paint.setAntiAlias(true);
          const SkRect border = SkRect::MakeIWH(kSegLen, kSegLen).makeInset(.5f, .5f);
          paint.setColor(SK_ColorBLUE);
          paint.setStyle(SkPaint::kStroke_Style);
          surface->getCanvas()->drawRect(border, paint);
  
          paint.setColor(SK_ColorBLACK);
          surface->getCanvas()->drawLine(start, end, paint);
  
          paint.reset();
          paint.setColor(color);
          const SkPoint pts[] = { start, end };
          surface->getCanvas()->drawPoints(SkCanvas::kPoints_PointMode, 2, pts, paint);
  
          return surface->makeImageSnapshot();
      }
  
      void drawSets(SkCanvas* canvas) const {
          SkAutoCanvasRestore acr(canvas, true);
  
          const SkFilterQuality filters[] = {
              kNone_SkFilterQuality,
              kLow_SkFilterQuality,
              kMedium_SkFilterQuality,
              kHigh_SkFilterQuality
          };
          const bool AAs[] = { false, true };
  
          SkPaint paint;
          for (int i = 0; i < fSets.count(); ++i) {
              auto& set = fSets[i];
              SkPoint lastPt;
              for (size_t j = 0; j < SK_ARRAY_COUNT(AAs); ++j) {
                  paint.setAntiAlias(AAs[j]);
                  for (size_t k = 0; k < SK_ARRAY_COUNT(filters); ++k) {
                      paint.setFilterQuality(filters[k]);
                      lastPt = drawSet(canvas, set, paint);
                      canvas->translate((kSegLen + 4) * set.fVector.y(),
                                        (kSegLen + 4) * set.fVector.x());
                  }
              }
              canvas->translate(lastPt.x() + kSegLen,
                  - SkIntToScalar(kSegLen + 4) * SK_ARRAY_COUNT(filters) * SK_ARRAY_COUNT(AAs));
          }
      }
  
      SkPoint drawSet(SkCanvas* canvas, const ImageSet& set, const SkPaint& paint) const {
          SkASSERT(set.fImages.count() == set.fScales.count());
  
          SkPoint pt = SkPoint::Make(0, 0);
          for (int i = 0; i < set.fImages.count(); ++i) {
              auto& img = set.fImages[i];
              const SkRect dst =
                  SkRect::MakeXYWH(pt.x(), pt.y(),
                      img->width() * (1 + (set.fScales[i] - 1) * set.fVector.x()),
                      img->height() * (1 + (set.fScales[i] - 1) * set.fVector.y()));
  
              canvas->drawImageRect(img.get(), dst, &paint);
              pt.offset(dst.width() * set.fVector.x(), dst.height() * set.fVector.y());
          }
  
          return pt;
      }
  
      static constexpr unsigned  kSegLen = 15;
      static constexpr unsigned  kStretchFactor = 4;
      SkSTArray<2, ImageSet> fSets;
  
      typedef GM INHERITED;
  };
  
  DEF_GM(return new ImageScaleAlignedGM();)
  