/*
 * 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 "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkRSXform.h"
#include "include/core/SkShader.h"
#include "include/core/SkSurface.h"
#include "include/core/SkTextBlob.h"
#include "tools/ToolUtils.h"

// Exercises RSX text blobs + shader with various local matrix combinations.
// Yellow grid should stay aligned for text vs. background.
class RSXShaderGM : public skiagm::GM {
public:
private:
    SkString onShortName() override {
        return SkString("rsx_blob_shader");
    }

    SkISize onISize() override {
        return SkISize::Make(kSZ*kScale*2.1f, kSZ*kScale*2.1f);
    }

    void onOnceBeforeDraw() override {
        const SkFontStyle style(SkFontStyle::kExtraBlack_Weight,
                                SkFontStyle::kNormal_Width,
                                SkFontStyle::kUpright_Slant);
        SkFont font(ToolUtils::create_portable_typeface(nullptr, style), kFontSZ);
        font.setEdging(SkFont::Edging::kAntiAlias);

        static constexpr char txt[] = "TEST";
        SkGlyphID glyphs[16];
        float     widths[16];
        const auto glyph_count = font.textToGlyphs(txt, strlen(txt), SkTextEncoding::kUTF8,
                                                   glyphs, SK_ARRAY_COUNT(glyphs));
        font.getWidths(glyphs, glyph_count, widths);

        SkTextBlobBuilder builder;
        const auto& buf = builder.allocRunRSXform(font, glyph_count);
        std::copy(glyphs, glyphs + glyph_count, buf.glyphs);

        float x = 0;
        for (int i = 0; i < glyph_count; ++i) {
            buf.xforms()[i] = {
                1, 0,
                x, 0,
            };
            x += widths[i];
        }

        fBlob = builder.make();
    }

    void onDraw(SkCanvas* canvas) override {
        canvas->scale(kScale, kScale);
        this->draw_one(canvas,
            {0, 0}, SkMatrix::I(), SkMatrix::I());
        this->draw_one(canvas,
            {kSZ*1.1f, 0}, SkMatrix::Scale(2, 2), SkMatrix::I());
        this->draw_one(canvas,
            {0, kSZ*1.1f}, SkMatrix::I(), SkMatrix::RotateDeg(45));
        this->draw_one(canvas,
            {kSZ*1.1f, kSZ*1.1f}, SkMatrix::Scale(2, 2), SkMatrix::RotateDeg(45));
    }

    void draw_one(SkCanvas* canvas, SkPoint pos, const SkMatrix& lm,
                  const SkMatrix& outer_lm) const {
        SkAutoCanvasRestore acr(canvas, true);
        canvas->translate(pos.fX, pos.fY);

        SkPaint p;
        p.setShader(make_shader(lm, outer_lm));
        p.setAlphaf(0.75f);
        canvas->drawRect(SkRect::MakeWH(kSZ, kSZ), p);

        p.setAlphaf(1);
        canvas->drawTextBlob(fBlob, 0, kFontSZ*1, p);
        canvas->drawTextBlob(fBlob, 0, kFontSZ*2, p);
    }

    static sk_sp<SkShader> make_shader(const SkMatrix& lm, const SkMatrix& outer_lm) {
        static constexpr SkISize kTileSize = { 30, 30 };
        auto surface = SkSurface::MakeRasterN32Premul(kTileSize.width(), kTileSize.height());

        SkPaint p;
        p.setColor(0xffffff00);
        surface->getCanvas()->drawPaint(p);
        p.setColor(0xff008000);
        surface->getCanvas()
               ->drawRect({0, 0, kTileSize.width()*0.9f, kTileSize.height()*0.9f}, p);

        return surface->makeImageSnapshot()
                ->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
                             SkSamplingOptions(SkFilterMode::kLinear), &lm)
                ->makeWithLocalMatrix(outer_lm);
    }

    static constexpr float kSZ     = 300,
                           kFontSZ = kSZ * 0.38,
                           kScale  = 1.4f;

    sk_sp<SkTextBlob> fBlob;
};

DEF_GM(return new RSXShaderGM;)