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

#include "../src/jumper/SkJumper.h"
#include "SkColorSpace_New.h"
#include "SkRasterPipeline.h"
#include "Test.h"
#include <initializer_list>

DEF_TEST(SkColorSpace_New_TransferFnBasics, r) {
    auto gamut = SkMatrix44::I();
    auto blending = SkColorSpace_New::Blending::AsEncoded;

    SkColorSpace_New linearA{SkColorSpace_New::TransferFn::MakeLinear(),    gamut, blending},
                     linearB{SkColorSpace_New::TransferFn::MakeGamma(1),    gamut, blending},
                        srgb{SkColorSpace_New::TransferFn::MakeSRGB(),      gamut, blending},
                       gamma{SkColorSpace_New::TransferFn::MakeGamma(2.2f), gamut, blending};

    REPORTER_ASSERT(r,  linearA.gammaIsLinear());
    REPORTER_ASSERT(r,  linearB.gammaIsLinear());
    REPORTER_ASSERT(r, !   srgb.gammaIsLinear());
    REPORTER_ASSERT(r, !  gamma.gammaIsLinear());

    REPORTER_ASSERT(r, !linearA.gammaCloseToSRGB());
    REPORTER_ASSERT(r, !linearB.gammaCloseToSRGB());
    REPORTER_ASSERT(r,     srgb.gammaCloseToSRGB());
    REPORTER_ASSERT(r, !  gamma.gammaCloseToSRGB());

    REPORTER_ASSERT(r,  linearA.transferFn().equals(linearB.transferFn()));
    REPORTER_ASSERT(r, !linearA.transferFn().equals(   srgb.transferFn()));
    REPORTER_ASSERT(r, !linearA.transferFn().equals(  gamma.transferFn()));
    REPORTER_ASSERT(r, !linearB.transferFn().equals(   srgb.transferFn()));
    REPORTER_ASSERT(r, !linearB.transferFn().equals(  gamma.transferFn()));
    REPORTER_ASSERT(r, !   srgb.transferFn().equals(  gamma.transferFn()));
}

DEF_TEST(SkColorSpace_New_TransferFnStages, r) {
    // We'll create a little SkRasterPipelineBlitter-like scenario,
    // blending the same src color over the same dst color, but with
    // three different transfer functions, for simplicity the same for src and dst.
    SkColor src = 0x7f7f0000;

    SkColor dsts[3];
    for (SkColor& dst : dsts) {
        dst = 0xff007f00;
    }

    auto gamut = SkMatrix44::I();
    auto blending = SkColorSpace_New::Blending::Linear;
    SkColorSpace_New linear{SkColorSpace_New::TransferFn::MakeLinear(), gamut, blending},
                       srgb{SkColorSpace_New::TransferFn::MakeSRGB(),   gamut, blending},
                      gamma{SkColorSpace_New::TransferFn::MakeGamma(3), gamut, blending};
    SkColor* dst = dsts;
    for (const SkColorSpace_New* cs : {&linear, &srgb, &gamma}) {
        SkJumper_MemoryCtx src_ctx = {  &src, 0 },
                           dst_ctx = { dst++, 0 };

        SkRasterPipeline_<256> p;

        p.append(SkRasterPipeline::load_8888, &src_ctx);
        cs->transferFn().linearizeSrc(&p);
        p.append(SkRasterPipeline::premul);

        p.append(SkRasterPipeline::load_8888_dst, &dst_ctx);
        cs->transferFn().linearizeDst(&p);
        p.append(SkRasterPipeline::premul_dst);

        p.append(SkRasterPipeline::srcover);
        p.append(SkRasterPipeline::unpremul);
        cs->transferFn().encodeSrc(&p);
        p.append(SkRasterPipeline::store_8888, &dst_ctx);
        p.run(0,0,1,1);
    }

    // Double check the uninteresting channels: alpha's opaque, no blue.
    REPORTER_ASSERT(r, SkColorGetA(dsts[0]) == 0xff && SkColorGetB(dsts[0]) == 0x00);
    REPORTER_ASSERT(r, SkColorGetA(dsts[1]) == 0xff && SkColorGetB(dsts[1]) == 0x00);
    REPORTER_ASSERT(r, SkColorGetA(dsts[2]) == 0xff && SkColorGetB(dsts[2]) == 0x00);

    // Because we're doing linear blending, a more-exponential transfer function will
    // brighten the encoded values more when linearizing. So we expect to see that
    // linear is darker than sRGB, and sRGB in turn is darker than gamma 3.
    REPORTER_ASSERT(r, SkColorGetR(dsts[0]) < SkColorGetR(dsts[1]));
    REPORTER_ASSERT(r, SkColorGetR(dsts[1]) < SkColorGetR(dsts[2]));

    REPORTER_ASSERT(r, SkColorGetG(dsts[0]) < SkColorGetG(dsts[1]));
    REPORTER_ASSERT(r, SkColorGetG(dsts[1]) < SkColorGetG(dsts[2]));

}