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

#include "include/encode/SkPngRustEncoder.h"

#include "include/codec/SkAndroidCodec.h"
#include "include/codec/SkCodec.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkPixmap.h"
#include "include/core/SkStream.h"
#include "include/private/SkGainmapInfo.h"
#include "tests/GainmapTestCommon.h"
#include "tests/Test.h"
#include "tools/DecodeUtils.h"
#include "tools/ToolUtils.h"

DEF_TEST(RustEncodePng_smoke_test, r) {
    SkBitmap bitmap;
    bool success = ToolUtils::GetResourceAsBitmap("images/mandrill_128.png", &bitmap);
    if (!success) {
        return;
    }

    SkPixmap src;
    success = bitmap.peekPixels(&src);
    REPORTER_ASSERT(r, success);
    if (!success) {
        return;
    }

    SkDynamicMemoryWStream dst;
    SkPngRustEncoder::Options options;
    success = SkPngRustEncoder::Encode(&dst, src, options);
    REPORTER_ASSERT(r, success);
    if (!success) {
        return;
    }

    SkBitmap roundtrip;
    success = ToolUtils::DecodeDataToBitmap(dst.detachAsData(), &roundtrip);
    REPORTER_ASSERT(r, success);
    if (!success) {
        return;
    }

    success = ToolUtils::equal_pixels(bitmap, roundtrip);
    REPORTER_ASSERT(r, success);
}

#ifdef SK_CODEC_USES_PNG_WITH_RUST_FOR_ANDROID
DEF_TEST(RustEncodePng_gainmap, r) {
    SkColorType colorTypes[] = {
            kRGBA_8888_SkColorType,
            kAlpha_8_SkColorType,
    };

    for (const auto& colorType : colorTypes) {
        SkDynamicMemoryWStream stream;
        SkGainmapInfo sourceGainmapInfo;
        sourceGainmapInfo.fGainmapRatioMin = {1.f, 1.f, 1.f, 1.f};
        sourceGainmapInfo.fGainmapRatioMax = {5.f, 5.f, 5.f, 1.f};
        sourceGainmapInfo.fGainmapGamma = {1.f, 1.f, 1.f, 1.f};
        sourceGainmapInfo.fEpsilonSdr = {0.01f, 0.01f, 0.01f, 0.01f};
        sourceGainmapInfo.fEpsilonHdr = {0.001f, 0.001f, 0.001f, 0.001f};
        sourceGainmapInfo.fDisplayRatioSdr = 1.f;
        sourceGainmapInfo.fDisplayRatioHdr = 3.f;
        sourceGainmapInfo.fGainmapMathColorSpace = SkColorSpace::MakeSRGB();
        SkBitmap sourceBase;
        sourceBase.allocPixels(
                SkImageInfo::Make(16, 16, kRGBA_8888_SkColorType, kOpaque_SkAlphaType));
        sourceBase.eraseColor(SK_ColorRED);
        SkBitmap sourceGainmap;
        sourceGainmap.allocPixels(SkImageInfo::Make(4, 4, colorType, kOpaque_SkAlphaType));
        sourceGainmap.eraseColor(SK_ColorGREEN);

        SkPngRustEncoder::Options options;
        options.fGainmap = &(sourceGainmap.pixmap());
        options.fGainmapInfo = &sourceGainmapInfo;
        REPORTER_ASSERT(r, SkPngRustEncoder::Encode(&stream, sourceBase.pixmap(), options));

        SkCodec::Result result = SkCodec::kSuccess;
        std::unique_ptr<SkCodec> baseCodec =
                SkCodec::MakeFromStream(stream.detachAsStream(), &result);
        REPORTER_ASSERT(r, baseCodec);
        if (!baseCodec) {
            continue;
        }

        SkBitmap baseBitmap;
        baseBitmap.allocPixels(baseCodec->getInfo());
        REPORTER_ASSERT(r,
                        SkCodec::kSuccess == baseCodec->getPixels(baseBitmap.info(),
                                                                  baseBitmap.getPixels(),
                                                                  baseBitmap.rowBytes()));

        std::unique_ptr<SkAndroidCodec> androidCodec =
                SkAndroidCodec::MakeFromCodec(std::move(baseCodec));
        REPORTER_ASSERT(r, androidCodec);
        if (!androidCodec) {
            continue;
        }

        SkGainmapInfo gainmapInfo;
        std::unique_ptr<SkAndroidCodec> gainmapCodec;
        bool decodedGainmap = androidCodec->getGainmapAndroidCodec(&gainmapInfo, &gainmapCodec);
        REPORTER_ASSERT(r, decodedGainmap);
        REPORTER_ASSERT(r, gainmapCodec);
        if (!gainmapCodec) {
            continue;
        }
        SkBitmap gainmapBitmap;
        gainmapBitmap.allocPixels(gainmapCodec->getInfo());
        REPORTER_ASSERT(
                r,
                SkCodec::kSuccess == gainmapCodec->getAndroidPixels(gainmapBitmap.info(),
                                                                    gainmapBitmap.getPixels(),
                                                                    gainmapBitmap.rowBytes()));

        REPORTER_ASSERT(r, baseBitmap.dimensions().fHeight == 16);
        REPORTER_ASSERT(r, baseBitmap.dimensions().fWidth == 16);
        REPORTER_ASSERT(r, baseBitmap.getColor(0, 0) == SK_ColorRED);

        REPORTER_ASSERT(r, gainmapBitmap.dimensions().fHeight == 4);
        REPORTER_ASSERT(r, gainmapBitmap.dimensions().fWidth == 4);

        if (colorType == kAlpha_8_SkColorType) {
            REPORTER_ASSERT(r, gainmapBitmap.getColor(0, 0) == SK_ColorBLACK);
        } else {
            REPORTER_ASSERT(r, gainmapBitmap.getColor(0, 0) == SK_ColorGREEN);
        }

        if (colorType == kAlpha_8_SkColorType) {
            sourceGainmapInfo.fGainmapMathColorSpace = nullptr;
        }

        skiatest::ExpectApproxEqInfo(r, sourceGainmapInfo, gainmapInfo);
    }
}

DEF_TEST(RustEncodePng_sBIT, r) {
    // Test kRGB_565_SkColorType
    {
        SkBitmap source;
        source.allocPixels(SkImageInfo::Make(16, 16, kRGB_565_SkColorType, kOpaque_SkAlphaType));
        source.eraseColor(SK_ColorRED);

        SkDynamicMemoryWStream stream;
        REPORTER_ASSERT(r, SkPngRustEncoder::Encode(&stream, source.pixmap(), {}));

        SkCodec::Result result = SkCodec::kSuccess;
        std::unique_ptr<SkCodec> codec = SkCodec::MakeFromStream(stream.detachAsStream(), &result);
        REPORTER_ASSERT(r, codec);
        if (codec) {
            // Verify that the decoder recommends kRGB_565_SkColorType because of the sBIT chunk.
            REPORTER_ASSERT(r, codec->getInfo().colorType() == kRGB_565_SkColorType);
        }
    }

    // Test kAlpha_8_SkColorType
    {
        SkBitmap source;
        source.allocPixels(SkImageInfo::Make(16, 16, kAlpha_8_SkColorType, kPremul_SkAlphaType));
        source.eraseColor(SK_ColorTRANSPARENT);  // Alpha 8 only cares about alpha channel

        SkDynamicMemoryWStream stream;
        REPORTER_ASSERT(r, SkPngRustEncoder::Encode(&stream, source.pixmap(), {}));

        SkCodec::Result result = SkCodec::kSuccess;
        std::unique_ptr<SkCodec> codec = SkCodec::MakeFromStream(stream.detachAsStream(), &result);
        REPORTER_ASSERT(r, codec);
        if (codec) {
            // Verify that the decoder recommends kAlpha_8_SkColorType because of the sBIT chunk.
            REPORTER_ASSERT(r, codec->getInfo().colorType() == kAlpha_8_SkColorType);
        }
    }
}
#endif
