/*
 * Copyright 2020 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/core/SkCanvas.h"
#include "include/core/SkFont.h"
#include "include/core/SkSurface.h"
#include "tools/viewer/Slide.h"

#include <chrono>

class TimingSlide : public Slide {
public:
    TimingSlide() { fName = "Timing";}

    void load(SkScalar w, SkScalar h) override {
        sk_sp<SkSurface> surf = SkSurface::MakeRasterN32Premul(W,H);
        surf->getCanvas()->drawString("abc", 2,H-4, SkFont{}, SkPaint{});
        fImg = surf->makeImageSnapshot();
    }

    void draw(SkCanvas* canvas) override {
        canvas->scale(8,8);

        // Draw normally.
        canvas->drawImage(fImg, 0,0);

        canvas->translate(0,H);

        // Draw one pixel at a time with drawImageRect(),
        // timing how long each drawImageRect() call takes.
        double cost[H][W];
        double min = +INFINITY,
               max = -INFINITY;
        for (int y = 0; y < H; y++)
        for (int x = 0; x < W; x++) {
            auto start = std::chrono::steady_clock::now();
            canvas->drawImageRect(fImg.get(),
                                  SkRect::MakeXYWH(x,y,1,1), SkRect::MakeXYWH(x,y,1,1),
                                  SkSamplingOptions(), /*paint=*/nullptr,
                                  SkCanvas::kStrict_SrcRectConstraint);
            auto elapsed = std::chrono::steady_clock::now() - start;

            cost[y][x] = elapsed.count();
            min = std::min(min, cost[y][x]);
            max = std::max(max, cost[y][x]);
        }

        canvas->translate(0,H);

        // Draw using those per-pixel timings,
        // with the slowest pixel scaled to alpha=1, the fastest to alpha=0.
        for (int y = 0; y < H; y++)
        for (int x = 0; x < W; x++) {
            SkPaint p;
            p.setAlphaf( (cost[y][x] - min) / (max - min) );
            canvas->drawRect(SkRect::MakeXYWH(x,y,1,1), p);
        }

        canvas->translate(0,H);

        // Draw each pixel into offscreen, timing each draw.
        SkImageInfo info = canvas->imageInfo().makeWH(1024,1024);
        if (sk_sp<SkSurface> offscreen = canvas->makeSurface(info)) {
            min = +INFINITY;
            max = -INFINITY;
            for (int y = 0; y < H; y++)
            for (int x = 0; x < W; x++) {
                auto start = std::chrono::steady_clock::now();
                offscreen->getCanvas()->drawImageRect(fImg,
                                                      SkRect::MakeXYWH(x,y,1,1),
                                                      SkRect::MakeXYWH(0,0,1024,1024),
                                                      SkSamplingOptions(),
                                                      /*paint=*/nullptr,
                                                      SkCanvas::kStrict_SrcRectConstraint);
                auto elapsed = std::chrono::steady_clock::now() - start;

                cost[y][x] = elapsed.count();
                min = std::min(min, cost[y][x]);
                max = std::max(max, cost[y][x]);
            }
            for (int y = 0; y < H; y++)
            for (int x = 0; x < W; x++) {
                SkPaint p;
                p.setAlphaf( (cost[y][x] - min) / (max - min) );
                canvas->drawRect(SkRect::MakeXYWH(x,y,1,1), p);
            }
        }
    }
private:
    inline static constexpr int W = 24,
                                H = 16;
    sk_sp<SkImage> fImg;
};

DEF_SLIDE( return new TimingSlide; )
