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

#include "src/ports/SkNDKConversions.h"

namespace {
static const struct {
    SkColorType         colorType;
    AndroidBitmapFormat format;
} gColorTypeTable[] = {
    { kRGBA_8888_SkColorType, ANDROID_BITMAP_FORMAT_RGBA_8888 },
    { kRGBA_F16_SkColorType,  ANDROID_BITMAP_FORMAT_RGBA_F16 },
    { kRGB_565_SkColorType,   ANDROID_BITMAP_FORMAT_RGB_565 },
    // Android allows using its alpha 8 format to get 8 bit gray pixels.
    { kGray_8_SkColorType,    ANDROID_BITMAP_FORMAT_A_8 },
};

} // anonymous namespace

namespace SkNDKConversions {
    AndroidBitmapFormat toAndroidBitmapFormat(SkColorType colorType) {
        for (const auto& entry : gColorTypeTable) {
            if (entry.colorType == colorType) {
                return entry.format;
            }
        }
        return ANDROID_BITMAP_FORMAT_NONE;
    }

    uint32_t toAndroidBitmapAlphaFlags(SkAlphaType alphaType) {
        switch (alphaType) {
            case kPremul_SkAlphaType:
                return ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
            case kOpaque_SkAlphaType:
                return ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE;
            case kUnpremul_SkAlphaType:
                return ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL;
            default:
                SkDEBUGFAIL("unspecified alphaType");
                return ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE;
        }
    }

    SkColorType toColorType(AndroidBitmapFormat format) {
        for (const auto& entry : gColorTypeTable) {
            if (entry.format == format) {
                return entry.colorType;
            }
        }
        return kUnknown_SkColorType;
    }

} // SkNDKConversions

static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};

static constexpr skcms_Matrix3x3 kDCIP3 = {{
        {0.486143, 0.323835, 0.154234},
        {0.226676, 0.710327, 0.0629966},
        {0.000800549, 0.0432385, 0.78275},
}};

namespace {
static const struct {
    ADataSpace             dataSpace;
    skcms_TransferFunction transferFunction;
    skcms_Matrix3x3        gamut;
} gColorSpaceTable[] = {
    { ADATASPACE_SRGB,         SkNamedTransferFn::kSRGB,    SkNamedGamut::kSRGB },
    { ADATASPACE_SCRGB,        SkNamedTransferFn::kSRGB,    SkNamedGamut::kSRGB },
    { ADATASPACE_SCRGB_LINEAR, SkNamedTransferFn::kLinear,  SkNamedGamut::kSRGB },
    { ADATASPACE_SRGB_LINEAR,  SkNamedTransferFn::kLinear,  SkNamedGamut::kSRGB },
    { ADATASPACE_ADOBE_RGB,    SkNamedTransferFn::k2Dot2,   SkNamedGamut::kAdobeRGB },
    { ADATASPACE_DISPLAY_P3,   SkNamedTransferFn::kSRGB,    SkNamedGamut::kDisplayP3 },
    { ADATASPACE_BT2020,       SkNamedTransferFn::kRec2020, SkNamedGamut::kRec2020 },
    { ADATASPACE_BT709,        SkNamedTransferFn::kRec2020, SkNamedGamut::kSRGB },
    { ADATASPACE_DCI_P3,       k2Dot6,                      kDCIP3 },
};

} // anonymous namespace

static bool nearly_equal(float a, float b) {
    return fabs(a - b) < .002f;
}

static bool nearly_equal(const skcms_TransferFunction& x, const skcms_TransferFunction& y) {
    return nearly_equal(x.g, y.g)
        && nearly_equal(x.a, y.a)
        && nearly_equal(x.b, y.b)
        && nearly_equal(x.c, y.c)
        && nearly_equal(x.d, y.d)
        && nearly_equal(x.e, y.e)
        && nearly_equal(x.f, y.f);
}

static bool nearly_equal(const skcms_Matrix3x3& a, const skcms_Matrix3x3& b) {
    for (int i = 0; i < 3; i++)
    for (int j = 0; j < 3; j++) {
        if (!nearly_equal(a.vals[i][j], b.vals[i][j])) return false;
    }
    return true;
}

namespace SkNDKConversions {
    ADataSpace toDataSpace(SkColorSpace* cs) {
        if (!cs) return ADATASPACE_SRGB;

        skcms_TransferFunction fn;
        skcms_Matrix3x3 gamut;
        if (cs->isNumericalTransferFn(&fn) && cs->toXYZD50(&gamut)) {
            for (const auto& entry : gColorSpaceTable) {
                if (nearly_equal(gamut, entry.gamut) && nearly_equal(fn, entry.transferFunction)) {
                    return entry.dataSpace;
                }
            }
        }
        return ADATASPACE_UNKNOWN;
    }

    sk_sp<SkColorSpace> toColorSpace(ADataSpace dataSpace) {
        for (const auto& entry : gColorSpaceTable) {
            if (entry.dataSpace == dataSpace) {
                return SkColorSpace::MakeRGB(entry.transferFunction, entry.gamut);
            }
        }
        return nullptr;
    }
}

