/*
 * 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/SkBlendMode.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkFont.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRSXform.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/SkTypeface.h"
#include "include/core/SkTypes.h"
#include "tools/ToolUtils.h"

// Create a square atlas of:
//   opaque white  |     opaque red
//  ------------------------------------
//   opaque green  |  transparent black
//
static sk_sp<SkImage> make_atlas(SkCanvas* caller, int atlasSize) {
    const int kBlockSize = atlasSize/2;

    SkImageInfo info = SkImageInfo::MakeN32Premul(atlasSize, atlasSize);
    auto        surface(ToolUtils::makeSurface(caller, info));
    SkCanvas* canvas = surface->getCanvas();

    SkPaint paint;
    paint.setBlendMode(SkBlendMode::kSrc);

    paint.setColor(SK_ColorWHITE);
    SkRect r = SkRect::MakeXYWH(0, 0,
                                SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
    canvas->drawRect(r, paint);

    paint.setColor(SK_ColorRED);
    r = SkRect::MakeXYWH(SkIntToScalar(kBlockSize), 0,
                         SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
    canvas->drawRect(r, paint);

    paint.setColor(SK_ColorGREEN);
    r = SkRect::MakeXYWH(0, SkIntToScalar(kBlockSize),
                         SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
    canvas->drawRect(r, paint);

    paint.setColor(SK_ColorTRANSPARENT);
    r = SkRect::MakeXYWH(SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize),
                         SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
    canvas->drawRect(r, paint);

    return surface->makeImageSnapshot();
}

// This GM tests the drawAtlas API with colors, different xfer modes
// and transparency in the atlas image
class DrawAtlasColorsGM : public skiagm::GM {
public:
    DrawAtlasColorsGM() {
        this->setBGColor(0xFFCCCCCC);
    }

protected:
    SkString onShortName() override {
        return SkString("draw-atlas-colors");
    }

    SkISize onISize() override {
        return SkISize::Make(kNumXferModes * (kAtlasSize + kPad) + kPad,
                             2 * kNumColors * (kAtlasSize + kPad) + kTextPad + kPad);
    }

    void onDraw(SkCanvas* canvas) override {
        const SkRect target = SkRect::MakeWH(SkIntToScalar(kAtlasSize), SkIntToScalar(kAtlasSize));

        auto atlas = make_atlas(canvas, kAtlasSize);

        const SkBlendMode gModes[] = {
            SkBlendMode::kClear,
            SkBlendMode::kSrc,
            SkBlendMode::kDst,
            SkBlendMode::kSrcOver,
            SkBlendMode::kDstOver,
            SkBlendMode::kSrcIn,
            SkBlendMode::kDstIn,
            SkBlendMode::kSrcOut,
            SkBlendMode::kDstOut,
            SkBlendMode::kSrcATop,
            SkBlendMode::kDstATop,
            SkBlendMode::kXor,
            SkBlendMode::kPlus,
            SkBlendMode::kModulate,
            SkBlendMode::kScreen,
            SkBlendMode::kOverlay,
            SkBlendMode::kDarken,
            SkBlendMode::kLighten,
            SkBlendMode::kColorDodge,
            SkBlendMode::kColorBurn,
            SkBlendMode::kHardLight,
            SkBlendMode::kSoftLight,
            SkBlendMode::kDifference,
            SkBlendMode::kExclusion,
            SkBlendMode::kMultiply,
            SkBlendMode::kHue,
            SkBlendMode::kSaturation,
            SkBlendMode::kColor,
            SkBlendMode::kLuminosity,
        };

        SkColor gColors[] = {
            SK_ColorWHITE,
            SK_ColorRED,
            0x88888888,         // transparent grey
            0x88000088          // transparent blue
        };

        const int numModes = std::size(gModes);
        SkASSERT(numModes == kNumXferModes);
        const int numColors = std::size(gColors);
        SkASSERT(numColors == kNumColors);
        SkRSXform xforms[numColors];
        SkRect rects[numColors];
        SkColor quadColors[numColors];

        SkPaint paint;
        paint.setAntiAlias(true);

        for (int i = 0; i < numColors; ++i) {
            xforms[i].set(1.0f, 0.0f, SkIntToScalar(kPad), i*(target.width()+kPad));
            rects[i] = target;
            quadColors[i] = gColors[i];
        }

        SkFont font(ToolUtils::create_portable_typeface(), kTextPad);

        for (int i = 0; i < numModes; ++i) {
            const char* label = SkBlendMode_Name(gModes[i]);
            canvas->drawString(label, i*(target.width()+kPad)+kPad, SkIntToScalar(kTextPad),
                               font, paint);
        }

        for (int i = 0; i < numModes; ++i) {
            canvas->save();
            canvas->translate(SkIntToScalar(i*(target.height()+kPad)),
                              SkIntToScalar(kTextPad+kPad));
            // w/o a paint
            canvas->drawAtlas(atlas.get(), xforms, rects, quadColors, numColors,
                              gModes[i], SkSamplingOptions(), nullptr, nullptr);
            canvas->translate(0.0f, numColors*(target.height()+kPad));
            // w a paint
            canvas->drawAtlas(atlas.get(), xforms, rects, quadColors, numColors,
                              gModes[i], SkSamplingOptions(), nullptr, &paint);
            canvas->restore();
        }
    }

private:
    inline static constexpr int kNumXferModes = 29;
    inline static constexpr int kNumColors = 4;
    inline static constexpr int kAtlasSize = 30;
    inline static constexpr int kPad = 2;
    inline static constexpr int kTextPad = 8;

    using INHERITED = GM;
};
DEF_GM( return new DrawAtlasColorsGM; )
