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

#include "SkCanvas.h"
#include "SkImage.h"
#include "SkShader.h"
#include "SkSurface.h"
#include "SkTypes.h"
#include "Test.h"

#include "GrContext.h"

static void test_bitmap_equality(skiatest::Reporter* reporter, SkBitmap& bm1, SkBitmap& bm2) {
    REPORTER_ASSERT(reporter, bm1.computeByteSize() == bm2.computeByteSize());
    REPORTER_ASSERT(reporter, 0 == memcmp(bm1.getPixels(), bm2.getPixels(), bm1.computeByteSize()));
}

static void paint_source(SkSurface* sourceSurface) {
    SkCanvas* sourceCanvas = sourceSurface->getCanvas();
    sourceCanvas->clear(0xFFDEDEDE);

    SkPaint paintColor;
    paintColor.setColor(0xFFFF0000);
    paintColor.setStyle(SkPaint::kFill_Style);

    SkRect rect = SkRect::MakeXYWH(
            SkIntToScalar(1),
            SkIntToScalar(0),
            SkIntToScalar(1),
            SkIntToScalar(sourceSurface->height()));

    sourceCanvas->drawRect(rect, paintColor);
}

static void run_shader_test(skiatest::Reporter* reporter, SkSurface* sourceSurface,
                            SkSurface* destinationSurface, SkImageInfo& info) {
    paint_source(sourceSurface);

    sk_sp<SkImage> sourceImage(sourceSurface->makeImageSnapshot());
    sk_sp<SkShader> sourceShader = sourceImage->makeShader(
            SkShader::kRepeat_TileMode,
            SkShader::kRepeat_TileMode);

    SkPaint paint;
    paint.setShader(sourceShader);

    SkCanvas* destinationCanvas = destinationSurface->getCanvas();
    destinationCanvas->clear(SK_ColorTRANSPARENT);
    destinationCanvas->drawPaint(paint);

    SkBitmap bmOrig;
    bmOrig.allocN32Pixels(info.width(), info.height());
    sourceSurface->readPixels(bmOrig, 0, 0);


    SkBitmap bm;
    bm.allocN32Pixels(info.width(), info.height());
    destinationSurface->readPixels(bm, 0, 0);

    test_bitmap_equality(reporter, bmOrig, bm);

    // Test with a translated shader
    SkMatrix matrix;
    matrix.setTranslate(SkIntToScalar(-1), SkIntToScalar(0));

    sk_sp<SkShader> sourceShaderTranslated = sourceImage->makeShader(
            SkShader::kRepeat_TileMode,
            SkShader::kRepeat_TileMode,
            &matrix);

    destinationCanvas->clear(SK_ColorTRANSPARENT);

    SkPaint paintTranslated;
    paintTranslated.setShader(sourceShaderTranslated);

    destinationCanvas->drawPaint(paintTranslated);

    SkBitmap bmt;
    bmt.allocN32Pixels(info.width(), info.height());
    destinationSurface->readPixels(bmt, 0, 0);

    //  Test correctness
    {
        for (int y = 0; y < info.height(); y++) {
            REPORTER_ASSERT(reporter, 0xFFFF0000 == bmt.getColor(0, y));

            for (int x = 1; x < info.width(); x++) {
                REPORTER_ASSERT(reporter, 0xFFDEDEDE == bmt.getColor(x, y));
            }
        }
    }
}

DEF_TEST(ImageNewShader, reporter) {
    SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5);

    auto sourceSurface(SkSurface::MakeRaster(info));
    auto destinationSurface(SkSurface::MakeRaster(info));

    run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info);
}

static void gpu_to_gpu(skiatest::Reporter* reporter, GrContext* context) {
    SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5);

    auto sourceSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
    auto destinationSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));

    run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info);
}

static void gpu_to_raster(skiatest::Reporter* reporter, GrContext* context) {
    SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5);

    auto sourceSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
    auto destinationSurface(SkSurface::MakeRaster(info));

    run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info);
}

static void raster_to_gpu(skiatest::Reporter* reporter, GrContext* context) {
    SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5);

    auto sourceSurface(SkSurface::MakeRaster(info));
    auto destinationSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));

    run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info);
}

DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageNewShader_GPU, reporter, ctxInfo) {
    //  GPU -> GPU
    gpu_to_gpu(reporter, ctxInfo.grContext());

    //  GPU -> RASTER
    gpu_to_raster(reporter, ctxInfo.grContext());

    //  RASTER -> GPU
    raster_to_gpu(reporter, ctxInfo.grContext());
}