/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "pixel_convert.h"

#include <map>
#include <mutex>
#ifndef _WIN32
#include "securec.h"
#else
#include "memory.h"
#endif
#include "pixel_convert_adapter.h"
#include "image_utils.h"
#include "pixel_map.h"

#include "image_log.h"

#ifdef __cplusplus
extern "C" {
#endif
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavutil/imgutils.h"
#include "libavcodec/avcodec.h"
#ifdef __cplusplus
}
#endif

#undef LOG_DOMAIN
#define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE

#undef LOG_TAG
#define LOG_TAG "PixelConvert"

namespace OHOS {
namespace Media {
using namespace std;
#if __BYTE_ORDER == __LITTLE_ENDIAN
constexpr bool IS_LITTLE_ENDIAN = true;
#else
constexpr bool IS_LITTLE_ENDIAN = false;
#endif
constexpr int32_t DMA_LINE_SIZE = 256;
static const uint8_t NUM_2 = 2;
constexpr uint8_t YUV420_P010_BYTES = 2;

static void AlphaTypeConvertOnRGB(uint32_t &A, uint32_t &R, uint32_t &G, uint32_t &B,
                                  const ProcFuncExtension &extension)
{
    switch (extension.alphaConvertType) {
        case AlphaConvertType::PREMUL_CONVERT_UNPREMUL:
            R = Unpremul255(R, A);
            G = Unpremul255(G, A);
            B = Unpremul255(B, A);
            break;
        case AlphaConvertType::PREMUL_CONVERT_OPAQUE:
            R = Unpremul255(R, A);
            G = Unpremul255(G, A);
            B = Unpremul255(B, A);
            A = ALPHA_OPAQUE;
            break;
        case AlphaConvertType::UNPREMUL_CONVERT_PREMUL:
            R = Premul255(R, A);
            G = Premul255(G, A);
            B = Premul255(B, A);
            break;
        case AlphaConvertType::UNPREMUL_CONVERT_OPAQUE:
            A = ALPHA_OPAQUE;
            break;
        default:
            break;
    }
}

static uint32_t FillARGB8888(uint32_t A, uint32_t R, uint32_t G, uint32_t B)
{
    if (IS_LITTLE_ENDIAN) {
        return ((B << SHIFT_24_BIT) | (G << SHIFT_16_BIT) | (R << SHIFT_8_BIT) | A);
    }
    return ((A << SHIFT_24_BIT) | (R << SHIFT_16_BIT) | (G << SHIFT_8_BIT) | B);
}

static uint32_t FillABGR8888(uint32_t A, uint32_t B, uint32_t G, uint32_t R)
{
    if (IS_LITTLE_ENDIAN) {
        return ((R << SHIFT_24_BIT) | (G << SHIFT_16_BIT) | (B << SHIFT_8_BIT) | A);
    }
    return ((A << SHIFT_24_BIT) | (B << SHIFT_16_BIT) | (G << SHIFT_8_BIT) | R);
}

static uint32_t FillRGBA8888(uint32_t R, uint32_t G, uint32_t B, uint32_t A)
{
    if (IS_LITTLE_ENDIAN) {
        return ((A << SHIFT_24_BIT) | (B << SHIFT_16_BIT) | (G << SHIFT_8_BIT) | R);
    }
    return ((R << SHIFT_24_BIT) | (G << SHIFT_16_BIT) | (B << SHIFT_8_BIT) | A);
}

static uint32_t FillBGRA8888(uint32_t B, uint32_t G, uint32_t R, uint32_t A)
{
    if (IS_LITTLE_ENDIAN) {
        return ((A << SHIFT_24_BIT) | (R << SHIFT_16_BIT) | (G << SHIFT_8_BIT) | B);
    }
    return ((B << SHIFT_24_BIT) | (G << SHIFT_16_BIT) | (R << SHIFT_8_BIT) | A);
}

static uint16_t FillRGB565(uint32_t R, uint32_t G, uint32_t B)
{
    if (IS_LITTLE_ENDIAN) {
        return ((B << SHIFT_11_BIT) | (G << SHIFT_5_BIT) | R);
    }
    return ((R << SHIFT_11_BIT) | (G << SHIFT_5_BIT) | B);
}

static uint64_t FillRGBAF16(float R, float G, float B, float A)
{
    uint64_t R16 = FloatToHalf(R);
    uint64_t G16 = FloatToHalf(G);
    uint64_t B16 = FloatToHalf(B);
    uint64_t A16 = FloatToHalf(A);
    if (IS_LITTLE_ENDIAN) {
        return ((A16 << SHIFT_48_BIT) | (R16 << SHIFT_32_BIT) | (G16 << SHIFT_16_BIT) | B16);
    }
    return ((B16 << SHIFT_48_BIT) | (G16 << SHIFT_32_BIT) | (R16 << SHIFT_16_BIT) | A16);
}

constexpr uint8_t BYTE_BITS = 8;
constexpr uint8_t BYTE_BITS_MAX_INDEX = 7;
template<typename T>
static void BitConvert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t white,
                       uint32_t black)
{
    destinationRow[0] = (*sourceRow & GET_8_BIT) ? white : black;
    uint32_t bitIndex = 0;
    uint8_t currentSource = 0;
    /*
     * 1 byte = 8 bit
     * 7: 8 bit index
     */
    for (uint32_t i = 1; i < sourceWidth; i++) {
        bitIndex = i % BYTE_BITS;
        currentSource = *(sourceRow + i / BYTE_BITS);
        if (bitIndex > BYTE_BITS_MAX_INDEX) {
            continue;
        }
        destinationRow[i] = ((currentSource >> (BYTE_BITS_MAX_INDEX - bitIndex)) & GET_1_BIT) ? white : black;
    }
}

static void BitConvertGray(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                           const ProcFuncExtension &extension)
{
    uint8_t *newDestinationRow = static_cast<uint8_t *>(destinationRow);
    BitConvert(newDestinationRow, sourceRow, sourceWidth, GRAYSCALE_WHITE, GRAYSCALE_BLACK);
}

static void BitConvertARGB8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                               const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    BitConvert(newDestinationRow, sourceRow, sourceWidth, ARGB_WHITE, ARGB_BLACK);
}

static void BitConvertRGB565(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                             const ProcFuncExtension &extension)
{
    uint16_t *newDestinationRow = static_cast<uint16_t *>(destinationRow);
    BitConvert(newDestinationRow, sourceRow, sourceWidth, RGB_WHITE, RGB_BLACK);
}

constexpr uint32_t BRANCH_GRAY_TO_ARGB8888 = 0x00000001;
constexpr uint32_t BRANCH_GRAY_TO_RGB565 = 0x00000002;
template<typename T>
static void GrayConvert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t branch)
{
    for (uint32_t i = 0; i < sourceWidth; i++) {
        uint32_t R = sourceRow[i];
        uint32_t G = sourceRow[i];
        uint32_t B = sourceRow[i];
        if (branch == BRANCH_GRAY_TO_ARGB8888) {
            uint32_t A = ALPHA_OPAQUE;
            destinationRow[i] = ((A << SHIFT_24_BIT) | (R << SHIFT_16_BIT) | (G << SHIFT_8_BIT) | B);
        } else if (branch == BRANCH_GRAY_TO_RGB565) {
            R = R >> SHIFT_3_BIT;
            G = G >> SHIFT_2_BIT;
            B = B >> SHIFT_3_BIT;
            destinationRow[i] = ((R << SHIFT_11_BIT) | (G << SHIFT_5_BIT) | B);
        } else {
            break;
        }
    }
}

static void GrayConvertARGB8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    GrayConvert(newDestinationRow, sourceRow, sourceWidth, BRANCH_GRAY_TO_ARGB8888);
}

static void GrayConvertRGB565(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                              const ProcFuncExtension &extension)
{
    uint16_t *newDestinationRow = static_cast<uint16_t *>(destinationRow);
    GrayConvert(newDestinationRow, sourceRow, sourceWidth, BRANCH_GRAY_TO_RGB565);
}

constexpr uint32_t BRANCH_ARGB8888 = 0x10000001;
constexpr uint32_t BRANCH_ALPHA = 0x10000002;
template<typename T>
static void GrayAlphaConvert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t branch,
                             const ProcFuncExtension &extension)
{
    for (uint32_t i = 0; i < sourceWidth; i++) {
        uint32_t A = sourceRow[1];
        uint32_t R = sourceRow[0];
        uint32_t G = sourceRow[0];
        uint32_t B = sourceRow[0];
        AlphaTypeConvertOnRGB(A, R, G, B, extension);
        if (branch == BRANCH_ARGB8888) {
            destinationRow[i] = FillARGB8888(A, R, G, B);
        } else if (branch == BRANCH_ALPHA) {
            destinationRow[i] = A;
        } else {
            break;
        }
        sourceRow += SIZE_2_BYTE;
    }
}

static void GrayAlphaConvertARGB8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                     const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    GrayAlphaConvert(newDestinationRow, sourceRow, sourceWidth, BRANCH_ARGB8888, extension);
}

static void GrayAlphaConvertAlpha(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint8_t *newDestinationRow = static_cast<uint8_t *>(destinationRow);
    GrayAlphaConvert(newDestinationRow, sourceRow, sourceWidth, BRANCH_ALPHA, extension);
}

constexpr uint32_t BRANCH_BGR888_TO_ARGB8888 = 0x20000001;
constexpr uint32_t BRANCH_BGR888_TO_RGBA8888 = 0x20000002;
constexpr uint32_t BRANCH_BGR888_TO_BGRA8888 = 0x20000003;
constexpr uint32_t BRANCH_BGR888_TO_RGB565 = 0x20000004;
constexpr uint32_t BRANCH_BGR888_TO_RGBAF16 = 0x20000005;
template<typename T>
static void BGR888Convert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t branch)
{
    for (uint32_t i = 0; i < sourceWidth; i++) {
        uint32_t R = sourceRow[2];
        uint32_t G = sourceRow[1];
        uint32_t B = sourceRow[0];
        uint32_t A = ALPHA_OPAQUE;
        if (branch == BRANCH_BGR888_TO_ARGB8888) {
            destinationRow[i] = FillARGB8888(A, R, G, B);
        } else if (branch == BRANCH_BGR888_TO_RGBA8888) {
            destinationRow[i] = FillRGBA8888(R, G, B, A);
        } else if (branch == BRANCH_BGR888_TO_BGRA8888) {
            destinationRow[i] = FillBGRA8888(B, G, R, A);
        } else if (branch == BRANCH_BGR888_TO_RGB565) {
            R = R >> SHIFT_3_BIT;
            G = G >> SHIFT_2_BIT;
            B = B >> SHIFT_3_BIT;
            destinationRow[i] = ((B << SHIFT_11_BIT) | (G << SHIFT_5_BIT) | R);
        } else if (branch == BRANCH_BGR888_TO_RGBAF16) {
            destinationRow[i] = FillRGBAF16(R, G, B, A);
        } else {
            break;
        }
        sourceRow += SIZE_3_BYTE;
    }
}

static void BGR888ConvertARGB8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    BGR888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_BGR888_TO_ARGB8888);
}

static void BGR888ConvertRGBA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    BGR888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_BGR888_TO_RGBA8888);
}

static void BGR888ConvertBGRA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    BGR888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_BGR888_TO_BGRA8888);
}

static void BGR888ConvertRGB565(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                const ProcFuncExtension &extension)
{
    uint16_t *newDestinationRow = static_cast<uint16_t *>(destinationRow);
    BGR888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_BGR888_TO_RGB565);
}

static void BGR888ConvertRGBAF16(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint64_t *newDestinationRow = static_cast<uint64_t *>(tmp);
    BGR888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_BGR888_TO_RGBAF16);
}

constexpr uint32_t BRANCH_RGB888_TO_ARGB8888 = 0x30000001;
constexpr uint32_t BRANCH_RGB888_TO_RGBA8888 = 0x30000002;
constexpr uint32_t BRANCH_RGB888_TO_BGRA8888 = 0x30000003;
constexpr uint32_t BRANCH_RGB888_TO_RGB565 = 0x30000004;
constexpr uint32_t BRANCH_RGB888_TO_RGBAF16 = 0x30000005;
template<typename T>
static void RGB888Convert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t branch)
{
    for (uint32_t i = 0; i < sourceWidth; i++) {
        uint32_t R = sourceRow[0];
        uint32_t G = sourceRow[1];
        uint32_t B = sourceRow[2];
        uint32_t A = ALPHA_OPAQUE;
        if (branch == BRANCH_RGB888_TO_ARGB8888) {
            destinationRow[i] = FillARGB8888(A, R, G, B);
        } else if (branch == BRANCH_RGB888_TO_RGBA8888) {
            destinationRow[i] = FillRGBA8888(R, G, B, A);
        } else if (branch == BRANCH_RGB888_TO_BGRA8888) {
            destinationRow[i] = FillBGRA8888(B, G, R, A);
        } else if (branch == BRANCH_RGB888_TO_RGB565) {
            R = R >> SHIFT_3_BIT;
            G = G >> SHIFT_2_BIT;
            B = B >> SHIFT_3_BIT;
            destinationRow[i] = FillRGB565(R, G, B);
        } else if (branch == BRANCH_RGB888_TO_RGBAF16) {
            destinationRow[i] = FillRGBAF16(R, G, B, A);
        } else {
            break;
        }
        sourceRow += SIZE_3_BYTE;
    }
}
static void RGB888ConvertARGB8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGB888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB888_TO_ARGB8888);
}

static void RGB888ConvertRGBA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGB888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB888_TO_RGBA8888);
}

static void RGB888ConvertBGRA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGB888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB888_TO_BGRA8888);
}

static void RGB888ConvertRGB565(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                const ProcFuncExtension &extension)
{
    uint16_t *newDestinationRow = static_cast<uint16_t *>(destinationRow);
    RGB888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB888_TO_RGB565);
}

static void RGB888ConvertRGBAF16(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint64_t *newDestinationRow = static_cast<uint64_t *>(tmp);
    RGB888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB888_TO_RGBAF16);
}
constexpr uint32_t BRANCH_RGBA8888_TO_RGBA8888_ALPHA = 0x40000001;
constexpr uint32_t BRANCH_RGBA8888_TO_ARGB8888 = 0x40000002;
constexpr uint32_t BRANCH_RGBA8888_TO_BGRA8888 = 0x40000003;
constexpr uint32_t BRANCH_RGBA8888_TO_RGB565 = 0x40000004;
constexpr uint32_t BRANCH_RGBA8888_TO_RGBAF16 = 0x40000005;
template<typename T>
static void RGBA8888Convert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t branch,
                            const ProcFuncExtension &extension)
{
    for (uint32_t i = 0; i < sourceWidth; i++) {
        uint32_t R = sourceRow[0];
        uint32_t G = sourceRow[1];
        uint32_t B = sourceRow[2];
        uint32_t A = sourceRow[3];
        AlphaTypeConvertOnRGB(A, R, G, B, extension);
        if (branch == BRANCH_RGBA8888_TO_RGBA8888_ALPHA) {
            destinationRow[i] = FillRGBA8888(R, G, B, A);
        } else if (branch == BRANCH_RGBA8888_TO_ARGB8888) {
            destinationRow[i] = FillARGB8888(A, R, G, B);
        } else if (branch == BRANCH_RGBA8888_TO_BGRA8888) {
            destinationRow[i] = FillBGRA8888(B, G, R, A);
        } else if (branch == BRANCH_RGBA8888_TO_RGB565) {
            R = R >> SHIFT_3_BIT;
            G = G >> SHIFT_2_BIT;
            B = B >> SHIFT_3_BIT;
            destinationRow[i] = FillRGB565(R, G, B);
        } else if (branch == BRANCH_RGBA8888_TO_RGBAF16) {
            destinationRow[i] = FillRGBAF16(R, G, B, A);
        } else {
            break;
        }
        sourceRow += SIZE_4_BYTE;
    }
}

static void RGBA8888ConvertRGBA8888Alpha(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                         const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGBA8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBA8888_TO_RGBA8888_ALPHA, extension);
}

static void RGBA8888ConvertARGB8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                    const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGBA8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBA8888_TO_ARGB8888, extension);
}
static void RGBA8888ConvertBGRA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                    const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGBA8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBA8888_TO_BGRA8888, extension);
}

static void RGBA8888ConvertRGB565(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint16_t *newDestinationRow = static_cast<uint16_t *>(destinationRow);
    RGBA8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBA8888_TO_RGB565, extension);
}

static void RGBA8888ConvertRGBAF16(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint64_t *newDestinationRow = static_cast<uint64_t *>(tmp);
    RGBA8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBA8888_TO_RGBAF16, extension);
}
constexpr uint32_t BRANCH_BGRA8888_TO_BGRA8888_ALPHA = 0x80000001;
constexpr uint32_t BRANCH_BGRA8888_TO_ARGB8888 = 0x80000002;
constexpr uint32_t BRANCH_BGRA8888_TO_RGBA8888 = 0x80000003;
constexpr uint32_t BRANCH_BGRA8888_TO_RGB565 = 0x80000004;
constexpr uint32_t BRANCH_BGRA8888_TO_RGBAF16 = 0x80000005;
template<typename T>
static void BGRA8888Convert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t branch,
                            const ProcFuncExtension &extension)
{
    for (uint32_t i = 0; i < sourceWidth; i++) {
        uint32_t B = sourceRow[0];
        uint32_t G = sourceRow[1];
        uint32_t R = sourceRow[2];
        uint32_t A = sourceRow[3];
        AlphaTypeConvertOnRGB(A, R, G, B, extension);
        if (branch == BRANCH_BGRA8888_TO_BGRA8888_ALPHA) {
            destinationRow[i] = FillBGRA8888(B, G, R, A);
        } else if (branch == BRANCH_BGRA8888_TO_ARGB8888) {
            destinationRow[i] = FillARGB8888(A, R, G, B);
        } else if (branch == BRANCH_BGRA8888_TO_RGBA8888) {
            destinationRow[i] = FillRGBA8888(R, G, B, A);
        } else if (branch == BRANCH_BGRA8888_TO_RGB565) {
            R = R >> SHIFT_3_BIT;
            G = G >> SHIFT_2_BIT;
            B = B >> SHIFT_3_BIT;
            destinationRow[i] = FillRGB565(R, G, B);
        } else if (branch == BRANCH_BGRA8888_TO_RGBAF16) {
            destinationRow[i] = FillRGBAF16(R, G, B, A);
        } else {
            break;
        }
        sourceRow += SIZE_4_BYTE;
    }
}

static void BGRA8888ConvertBGRA8888Alpha(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                         const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    BGRA8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_BGRA8888_TO_BGRA8888_ALPHA, extension);
}

static void BGRA8888ConvertARGB8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                    const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    BGRA8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_BGRA8888_TO_ARGB8888, extension);
}

static void BGRA8888ConvertRGBA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                    const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    BGRA8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_BGRA8888_TO_RGBA8888, extension);
}

static void BGRA8888ConvertRGB565(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint16_t *newDestinationRow = static_cast<uint16_t *>(destinationRow);
    BGRA8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_BGRA8888_TO_RGB565, extension);
}

static void BGRA8888ConvertRGBAF16(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint64_t *newDestinationRow = static_cast<uint64_t *>(tmp);
    BGRA8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_BGRA8888_TO_RGBAF16, extension);
}

constexpr uint32_t BRANCH_ARGB8888_TO_ARGB8888_ALPHA = 0x90000001;
constexpr uint32_t BRANCH_ARGB8888_TO_RGBA8888 = 0x90000002;
constexpr uint32_t BRANCH_ARGB8888_TO_BGRA8888 = 0x90000003;
constexpr uint32_t BRANCH_ARGB8888_TO_RGB565 = 0x90000004;
constexpr uint32_t BRANCH_ARGB8888_TO_RGBAF16 = 0x90000005;
template<typename T>
static void ARGB8888Convert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t branch,
                            const ProcFuncExtension &extension)
{
    for (uint32_t i = 0; i < sourceWidth; i++) {
        uint32_t A = sourceRow[0];
        uint32_t R = sourceRow[1];
        uint32_t G = sourceRow[2];
        uint32_t B = sourceRow[3];
        AlphaTypeConvertOnRGB(A, R, G, B, extension);
        if (branch == BRANCH_ARGB8888_TO_ARGB8888_ALPHA) {
            destinationRow[i] = FillARGB8888(A, R, G, B);
        } else if (branch == BRANCH_ARGB8888_TO_RGBA8888) {
            destinationRow[i] = FillRGBA8888(R, G, B, A);
        } else if (branch == BRANCH_ARGB8888_TO_BGRA8888) {
            destinationRow[i] = FillBGRA8888(B, G, R, A);
        } else if (branch == BRANCH_ARGB8888_TO_RGB565) {
            R = R >> SHIFT_3_BIT;
            G = G >> SHIFT_2_BIT;
            B = B >> SHIFT_3_BIT;
            destinationRow[i] = FillRGB565(R, G, B);
        } else if (branch == BRANCH_ARGB8888_TO_RGBAF16) {
            destinationRow[i] = FillRGBAF16(R, G, B, A);
        } else {
            break;
        }
        sourceRow += SIZE_4_BYTE;
    }
}

static void ARGB8888ConvertARGB8888Alpha(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                         const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    ARGB8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_ARGB8888_TO_ARGB8888_ALPHA, extension);
}

static void ARGB8888ConvertRGBA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                    const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    ARGB8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_ARGB8888_TO_RGBA8888, extension);
}

static void ARGB8888ConvertBGRA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                    const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    ARGB8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_ARGB8888_TO_BGRA8888, extension);
}

static void ARGB8888ConvertRGB565(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint16_t *newDestinationRow = static_cast<uint16_t *>(destinationRow);
    ARGB8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_ARGB8888_TO_RGB565, extension);
}

static void ARGB8888ConvertRGBAF16(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint64_t *newDestinationRow = static_cast<uint64_t *>(tmp);
    ARGB8888Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_ARGB8888_TO_RGBAF16, extension);
}

constexpr uint32_t BRANCH_RGB161616_TO_ARGB8888 = 0x50000001;
constexpr uint32_t BRANCH_RGB161616_TO_ABGR8888 = 0x50000002;
constexpr uint32_t BRANCH_RGB161616_TO_RGBA8888 = 0x50000003;
constexpr uint32_t BRANCH_RGB161616_TO_BGRA8888 = 0x50000004;
constexpr uint32_t BRANCH_RGB161616_TO_RGB565 = 0x50000005;
constexpr uint32_t BRANCH_RGB161616_TO_RGBAF16 = 0x50000006;
template<typename T>
static void RGB161616Convert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t branch)
{
    for (uint32_t i = 0; i < sourceWidth; i++) {
        uint32_t R = sourceRow[0];
        uint32_t G = sourceRow[2];
        uint32_t B = sourceRow[4];
        uint32_t A = ALPHA_OPAQUE;
        if (branch == BRANCH_RGB161616_TO_ARGB8888) {
            destinationRow[i] = FillARGB8888(A, R, G, B);
        } else if (branch == BRANCH_RGB161616_TO_ABGR8888) {
            destinationRow[i] = FillABGR8888(A, B, G, R);
        } else if (branch == BRANCH_RGB161616_TO_RGBA8888) {
            destinationRow[i] = FillRGBA8888(R, G, B, A);
        } else if (branch == BRANCH_RGB161616_TO_BGRA8888) {
            destinationRow[i] = FillBGRA8888(B, G, R, A);
        } else if (branch == BRANCH_RGB161616_TO_RGB565) {
            R = R >> SHIFT_3_BIT;
            G = G >> SHIFT_2_BIT;
            B = B >> SHIFT_3_BIT;
            destinationRow[i] = FillRGB565(R, G, B);
        } else if (branch == BRANCH_RGB161616_TO_RGBAF16) {
            destinationRow[i] = FillRGBAF16(R, G, B, A);
        } else {
            break;
        }
        sourceRow += SIZE_6_BYTE;
    }
}

static void RGB161616ConvertARGB8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                     const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGB161616Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB161616_TO_ARGB8888);
}

static void RGB161616ConvertABGR8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                     const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGB161616Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB161616_TO_ABGR8888);
}

static void RGB161616ConvertRGBA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                     const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGB161616Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB161616_TO_RGBA8888);
}

static void RGB161616ConvertBGRA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                     const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGB161616Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB161616_TO_BGRA8888);
}

static void RGB161616ConvertRGB565(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                   const ProcFuncExtension &extension)
{
    uint16_t *newDestinationRow = static_cast<uint16_t *>(destinationRow);
    RGB161616Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB161616_TO_RGB565);
}

static void RGB161616ConvertRGBAF16(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint64_t *newDestinationRow = static_cast<uint64_t *>(tmp);
    RGB161616Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB161616_TO_RGBAF16);
}

constexpr uint32_t BRANCH_RGBA16161616_TO_ARGB8888 = 0x60000001;
constexpr uint32_t BRANCH_RGBA16161616_TO_ABGR8888 = 0x60000002;
constexpr uint32_t BRANCH_RGBA16161616_TO_RGBA8888 = 0x60000003;
constexpr uint32_t BRANCH_RGBA16161616_TO_BGRA8888 = 0x60000004;
constexpr uint32_t BRANCH_RGBA16161616_TO_RGBAF16 = 0x60000005;
template<typename T>
static void RGBA16161616Convert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t branch,
                                const ProcFuncExtension &extension)
{
    for (uint32_t i = 0; i < sourceWidth; i++) {
        uint32_t R = sourceRow[0];
        uint32_t G = sourceRow[2];
        uint32_t B = sourceRow[4];
        uint32_t A = sourceRow[6];
        AlphaTypeConvertOnRGB(A, R, G, B, extension);
        if (branch == BRANCH_RGBA16161616_TO_ARGB8888) {
            destinationRow[i] = FillARGB8888(A, R, G, B);
        } else if (branch == BRANCH_RGBA16161616_TO_ABGR8888) {
            destinationRow[i] = FillABGR8888(A, B, G, R);
        } else if (branch == BRANCH_RGBA16161616_TO_RGBA8888) {
            destinationRow[i] = FillRGBA8888(A, B, G, R);
        } else if (branch == BRANCH_RGBA16161616_TO_BGRA8888) {
            destinationRow[i] = FillBGRA8888(A, B, G, R);
        } else if (branch == BRANCH_RGBA16161616_TO_RGBAF16) {
            destinationRow[i] = FillRGBAF16(R, G, B, A);
        } else {
            break;
        }
        sourceRow += SIZE_8_BYTE;
    }
}

static void RGBA16161616ConvertARGB8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                        const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGBA16161616Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBA16161616_TO_ARGB8888, extension);
}

static void RGBA16161616ConvertABGR8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                        const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGBA16161616Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBA16161616_TO_ABGR8888, extension);
}

static void RGBA16161616ConvertRGBA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                        const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGBA16161616Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBA16161616_TO_RGBA8888, extension);
}

static void RGBA16161616ConvertBGRA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                        const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGBA16161616Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBA16161616_TO_BGRA8888, extension);
}

static void RGBA16161616ConvertRGBAF16(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint64_t *newDestinationRow = static_cast<uint64_t *>(tmp);
    RGBA16161616Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBA16161616_TO_RGBAF16, extension);
}

constexpr uint32_t BRANCH_CMYK_TO_ARGB8888 = 0x70000001;
constexpr uint32_t BRANCH_CMYK_TO_ABGR8888 = 0x70000002;
constexpr uint32_t BRANCH_CMYK_TO_RGBA8888 = 0x70000003;
constexpr uint32_t BRANCH_CMYK_TO_BGRA8888 = 0x70000004;
constexpr uint32_t BRANCH_CMYK_TO_RGB565 = 0x70000005;
template<typename T>
static void CMYKConvert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t branch)
{
    for (uint32_t i = 0; i < sourceWidth; i++) {
        uint8_t C = sourceRow[0];
        uint8_t M = sourceRow[1];
        uint8_t Y = sourceRow[2];
        uint8_t K = sourceRow[3];
        uint32_t R = Premul255(C, K);
        uint32_t G = Premul255(M, K);
        uint32_t B = Premul255(Y, K);
        uint32_t A = ALPHA_OPAQUE;
        if (branch == BRANCH_CMYK_TO_ARGB8888) {
            destinationRow[i] = FillARGB8888(A, R, G, B);
        } else if (branch == BRANCH_CMYK_TO_ABGR8888) {
            destinationRow[i] = FillABGR8888(A, B, G, R);
        } else if (branch == BRANCH_CMYK_TO_RGBA8888) {
            destinationRow[i] = FillRGBA8888(R, G, B, A);
        } else if (branch == BRANCH_CMYK_TO_BGRA8888) {
            destinationRow[i] = FillBGRA8888(B, G, R, A);
        } else if (branch == BRANCH_CMYK_TO_RGB565) {
            R = R >> SHIFT_3_BIT;
            G = R >> SHIFT_2_BIT;
            B = B >> SHIFT_3_BIT;
            destinationRow[i] = FillRGB565(R, G, B);
        } else {
            break;
        }
        sourceRow += SIZE_4_BYTE;
    }
}

static void CMYKConvertARGB8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    CMYKConvert(newDestinationRow, sourceRow, sourceWidth, BRANCH_CMYK_TO_ARGB8888);
}

static void CMYKConvertABGR8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    CMYKConvert(newDestinationRow, sourceRow, sourceWidth, BRANCH_CMYK_TO_ABGR8888);
}

static void CMYKConvertRGBA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    CMYKConvert(newDestinationRow, sourceRow, sourceWidth, BRANCH_CMYK_TO_RGBA8888);
}

static void CMYKConvertBGRA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    CMYKConvert(newDestinationRow, sourceRow, sourceWidth, BRANCH_CMYK_TO_BGRA8888);
}

static void CMYKConvertRGB565(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                              const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    CMYKConvert(newDestinationRow, sourceRow, sourceWidth, BRANCH_CMYK_TO_RGB565);
}

constexpr uint32_t BRANCH_RGB565_TO_ARGB8888 = 0x11000001;
constexpr uint32_t BRANCH_RGB565_TO_RGBA8888 = 0x11000002;
constexpr uint32_t BRANCH_RGB565_TO_BGRA8888 = 0x11000003;
constexpr uint32_t BRANCH_RGB565_TO_RGBAF16 = 0x11000004;
template<typename T>
static void RGB565Convert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t branch)
{
    for (uint32_t i = 0; i < sourceWidth; i++) {
        uint32_t R = (sourceRow[0] >> SHIFT_3_BIT) & SHIFT_5_MASK;
        uint32_t G = ((sourceRow[0] & SHIFT_3_MASK) << SHIFT_3_BIT) | ((sourceRow[1] >> SHIFT_5_BIT) & SHIFT_3_MASK);
        uint32_t B = sourceRow[1] & SHIFT_5_MASK;
        uint32_t A = ALPHA_OPAQUE;
        if (branch == BRANCH_RGB565_TO_ARGB8888) {
            destinationRow[i] = FillARGB8888(A, R, G, B);
        } else if (branch == BRANCH_RGB565_TO_RGBA8888) {
            destinationRow[i] = FillRGBA8888(R, G, B, A);
        } else if (branch == BRANCH_RGB565_TO_BGRA8888) {
            destinationRow[i] = FillBGRA8888(B, G, R, A);
        } else if (branch == BRANCH_RGB565_TO_RGBAF16) {
            destinationRow[i] = FillRGBAF16(R, G, B, A);
        } else {
            break;
        }
        sourceRow += SIZE_2_BYTE;
    }
}

static void RGB565ConvertARGB8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGB565Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB565_TO_ARGB8888);
}

static void RGB565ConvertRGBA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGB565Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB565_TO_RGBA8888);
}

static void RGB565ConvertBGRA8888(void *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
                                  const ProcFuncExtension &extension)
{
    uint32_t *newDestinationRow = static_cast<uint32_t *>(destinationRow);
    RGB565Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB565_TO_BGRA8888);
}

static void RGB565ConvertRGBAF16(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint64_t *newDestinationRow = static_cast<uint64_t *>(tmp);
    RGB565Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGB565_TO_RGBAF16);
}

constexpr uint32_t BRANCH_RGBAF16_TO_ARGB8888 = 0x13000001;
constexpr uint32_t BRANCH_RGBAF16_TO_RGBA8888 = 0x13000002;
constexpr uint32_t BRANCH_RGBAF16_TO_BGRA8888 = 0x13000003;
constexpr uint32_t BRANCH_RGBAF16_TO_ABGR8888 = 0x13000004;
constexpr uint32_t BRANCH_RGBAF16_TO_RGB565 = 0x13000005;
template<typename T>
static void RGBAF16Convert(T *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth, uint32_t branch,
                           const ProcFuncExtension &extension)
{
    for (uint32_t i = 0; i < sourceWidth; i++) {
        uint32_t R = HalfToUint32(sourceRow, IS_LITTLE_ENDIAN);
        uint32_t G = HalfToUint32(sourceRow + 2, IS_LITTLE_ENDIAN);
        uint32_t B = HalfToUint32(sourceRow + 4, IS_LITTLE_ENDIAN);
        uint32_t A = HalfToUint32(sourceRow + 6, IS_LITTLE_ENDIAN);
        AlphaTypeConvertOnRGB(A, R, G, B, extension);
        if (branch == BRANCH_RGBAF16_TO_ARGB8888) {
            destinationRow[i] = FillARGB8888(A, R, G, B);
        } else if (branch == BRANCH_RGBAF16_TO_RGBA8888) {
            destinationRow[i] = FillRGBA8888(R, G, B, A);
        } else if (branch == BRANCH_RGBAF16_TO_BGRA8888) {
            destinationRow[i] = FillBGRA8888(B, G, R, A);
        } else if (branch == BRANCH_RGBAF16_TO_ABGR8888) {
            destinationRow[i] = FillABGR8888(A, B, G, R);
        } else if (branch == BRANCH_RGBAF16_TO_RGB565) {
            R = R >> SHIFT_3_BIT;
            G = G >> SHIFT_2_BIT;
            B = B >> SHIFT_3_BIT;
            destinationRow[i] = FillRGB565(R, G, B);
        } else {
            break;
        }
        sourceRow += SIZE_8_BYTE;
    }
}

static void RGBAF16ConvertARGB8888(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint32_t *newDestinationRow = static_cast<uint32_t *>(tmp);
    RGBAF16Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBAF16_TO_ARGB8888, extension);
}

static void RGBAF16ConvertRGBA8888(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint32_t *newDestinationRow = static_cast<uint32_t *>(tmp);
    RGBAF16Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBAF16_TO_RGBA8888, extension);
}

static void RGBAF16ConvertBGRA8888(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint32_t *newDestinationRow = static_cast<uint32_t *>(tmp);
    RGBAF16Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBAF16_TO_BGRA8888, extension);
}

static void RGBAF16ConvertABGR8888(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint32_t *newDestinationRow = static_cast<uint32_t *>(tmp);
    RGBAF16Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBAF16_TO_ABGR8888, extension);
}

static void RGBAF16ConvertRGB565(uint8_t *destinationRow, const uint8_t *sourceRow, uint32_t sourceWidth,
    const ProcFuncExtension &extension)
{
    void* tmp = static_cast<void *>(destinationRow);
    uint16_t *newDestinationRow = static_cast<uint16_t *>(tmp);
    RGBAF16Convert(newDestinationRow, sourceRow, sourceWidth, BRANCH_RGBAF16_TO_RGB565, extension);
}

static map<string, ProcFuncType> g_procMapping;
static mutex g_procMutex;

static string MakeKey(uint32_t srcFormat, uint32_t dstFormat)
{
    return to_string(srcFormat) + ("_") + to_string(dstFormat);
}

static void InitGrayProc()
{
    g_procMapping.emplace(MakeKey(GRAY_BIT, ARGB_8888), &BitConvertARGB8888);
    g_procMapping.emplace(MakeKey(GRAY_BIT, RGB_565), &BitConvertRGB565);
    g_procMapping.emplace(MakeKey(GRAY_BIT, ALPHA_8), &BitConvertGray);

    g_procMapping.emplace(MakeKey(ALPHA_8, ARGB_8888), &GrayConvertARGB8888);
    g_procMapping.emplace(MakeKey(ALPHA_8, RGB_565), &GrayConvertRGB565);

    g_procMapping.emplace(MakeKey(GRAY_ALPHA, ARGB_8888), &GrayAlphaConvertARGB8888);
    g_procMapping.emplace(MakeKey(GRAY_ALPHA, ALPHA_8), &GrayAlphaConvertAlpha);
}

static void InitRGBProc()
{
    g_procMapping.emplace(MakeKey(RGB_888, ARGB_8888), &RGB888ConvertARGB8888);
    g_procMapping.emplace(MakeKey(RGB_888, RGBA_8888), &RGB888ConvertRGBA8888);
    g_procMapping.emplace(MakeKey(RGB_888, BGRA_8888), &RGB888ConvertBGRA8888);
    g_procMapping.emplace(MakeKey(RGB_888, RGB_565), &RGB888ConvertRGB565);

    g_procMapping.emplace(MakeKey(BGR_888, ARGB_8888), &BGR888ConvertARGB8888);
    g_procMapping.emplace(MakeKey(BGR_888, RGBA_8888), &BGR888ConvertRGBA8888);
    g_procMapping.emplace(MakeKey(BGR_888, BGRA_8888), &BGR888ConvertBGRA8888);
    g_procMapping.emplace(MakeKey(BGR_888, RGB_565), &BGR888ConvertRGB565);

    g_procMapping.emplace(MakeKey(RGB_161616, ARGB_8888), &RGB161616ConvertARGB8888);
    g_procMapping.emplace(MakeKey(RGB_161616, ABGR_8888), &RGB161616ConvertABGR8888);
    g_procMapping.emplace(MakeKey(RGB_161616, RGBA_8888), &RGB161616ConvertRGBA8888);
    g_procMapping.emplace(MakeKey(RGB_161616, BGRA_8888), &RGB161616ConvertBGRA8888);
    g_procMapping.emplace(MakeKey(RGB_161616, RGB_565), &RGB161616ConvertRGB565);

    g_procMapping.emplace(MakeKey(RGB_565, ARGB_8888), &RGB565ConvertARGB8888);
    g_procMapping.emplace(MakeKey(RGB_565, RGBA_8888), &RGB565ConvertRGBA8888);
    g_procMapping.emplace(MakeKey(RGB_565, BGRA_8888), &RGB565ConvertBGRA8888);
}

static void InitRGBAProc()
{
    g_procMapping.emplace(MakeKey(RGBA_8888, RGBA_8888), &RGBA8888ConvertRGBA8888Alpha);
    g_procMapping.emplace(MakeKey(RGBA_8888, ARGB_8888), &RGBA8888ConvertARGB8888);
    g_procMapping.emplace(MakeKey(RGBA_8888, BGRA_8888), &RGBA8888ConvertBGRA8888);
    g_procMapping.emplace(MakeKey(RGBA_8888, RGB_565), &RGBA8888ConvertRGB565);

    g_procMapping.emplace(MakeKey(BGRA_8888, RGBA_8888), &BGRA8888ConvertRGBA8888);
    g_procMapping.emplace(MakeKey(BGRA_8888, ARGB_8888), &BGRA8888ConvertARGB8888);
    g_procMapping.emplace(MakeKey(BGRA_8888, BGRA_8888), &BGRA8888ConvertBGRA8888Alpha);
    g_procMapping.emplace(MakeKey(BGRA_8888, RGB_565), &BGRA8888ConvertRGB565);

    g_procMapping.emplace(MakeKey(ARGB_8888, RGBA_8888), &ARGB8888ConvertRGBA8888);
    g_procMapping.emplace(MakeKey(ARGB_8888, ARGB_8888), &ARGB8888ConvertARGB8888Alpha);
    g_procMapping.emplace(MakeKey(ARGB_8888, BGRA_8888), &ARGB8888ConvertBGRA8888);
    g_procMapping.emplace(MakeKey(ARGB_8888, RGB_565), &ARGB8888ConvertRGB565);

    g_procMapping.emplace(MakeKey(RGBA_16161616, ARGB_8888), &RGBA16161616ConvertARGB8888);
    g_procMapping.emplace(MakeKey(RGBA_16161616, RGBA_8888), &RGBA16161616ConvertRGBA8888);
    g_procMapping.emplace(MakeKey(RGBA_16161616, BGRA_8888), &RGBA16161616ConvertBGRA8888);
    g_procMapping.emplace(MakeKey(RGBA_16161616, ABGR_8888), &RGBA16161616ConvertABGR8888);
}

static void InitCMYKProc()
{
    g_procMapping.emplace(MakeKey(CMKY, ARGB_8888), &CMYKConvertARGB8888);
    g_procMapping.emplace(MakeKey(CMKY, RGBA_8888), &CMYKConvertRGBA8888);
    g_procMapping.emplace(MakeKey(CMKY, BGRA_8888), &CMYKConvertBGRA8888);
    g_procMapping.emplace(MakeKey(CMKY, ABGR_8888), &CMYKConvertABGR8888);
    g_procMapping.emplace(MakeKey(CMKY, RGB_565), &CMYKConvertRGB565);
}

static void InitF16Proc()
{
    g_procMapping.emplace(MakeKey(RGBA_F16, ARGB_8888),
        reinterpret_cast<ProcFuncType>(&RGBAF16ConvertARGB8888));
    g_procMapping.emplace(MakeKey(RGBA_F16, RGBA_8888),
        reinterpret_cast<ProcFuncType>(&RGBAF16ConvertRGBA8888));
    g_procMapping.emplace(MakeKey(RGBA_F16, BGRA_8888),
        reinterpret_cast<ProcFuncType>(&RGBAF16ConvertBGRA8888));
    g_procMapping.emplace(MakeKey(RGBA_F16, ABGR_8888),
        reinterpret_cast<ProcFuncType>(&RGBAF16ConvertABGR8888));
    g_procMapping.emplace(MakeKey(RGBA_F16, RGB_565),
        reinterpret_cast<ProcFuncType>(&RGBAF16ConvertRGB565));

    g_procMapping.emplace(MakeKey(BGR_888, RGBA_F16),
        reinterpret_cast<ProcFuncType>(&BGR888ConvertRGBAF16));
    g_procMapping.emplace(MakeKey(RGB_888, RGBA_F16),
        reinterpret_cast<ProcFuncType>(&RGB888ConvertRGBAF16));
    g_procMapping.emplace(MakeKey(RGB_161616, RGBA_F16),
        reinterpret_cast<ProcFuncType>(&RGB161616ConvertRGBAF16));
    g_procMapping.emplace(MakeKey(ARGB_8888, RGBA_F16),
        reinterpret_cast<ProcFuncType>(&ARGB8888ConvertRGBAF16));
    g_procMapping.emplace(MakeKey(RGBA_8888, RGBA_F16),
        reinterpret_cast<ProcFuncType>(&RGBA8888ConvertRGBAF16));
    g_procMapping.emplace(MakeKey(BGRA_8888, RGBA_F16),
        reinterpret_cast<ProcFuncType>(&BGRA8888ConvertRGBAF16));
    g_procMapping.emplace(MakeKey(RGB_565, RGBA_F16),
        reinterpret_cast<ProcFuncType>(&RGB565ConvertRGBAF16));
    g_procMapping.emplace(MakeKey(RGBA_16161616, RGBA_F16),
        reinterpret_cast<ProcFuncType>(&RGBA16161616ConvertRGBAF16));
}

static ProcFuncType GetProcFuncType(uint32_t srcPixelFormat, uint32_t dstPixelFormat)
{
    unique_lock<mutex> guard(g_procMutex);
    if (g_procMapping.empty()) {
        InitGrayProc();
        InitRGBProc();
        InitRGBAProc();
        InitCMYKProc();
        InitF16Proc();
    }
    guard.unlock();
    string procKey = MakeKey(srcPixelFormat, dstPixelFormat);
    map<string, ProcFuncType>::iterator iter = g_procMapping.find(procKey);
    if (iter != g_procMapping.end()) {
        return iter->second;
    }
    return nullptr;
}

static AVPixelFormat PixelFormatToAVPixelFormat(const PixelFormat &pixelFormat)
{
    auto formatSearch = PixelConvertAdapter::FFMPEG_PIXEL_FORMAT_MAP.find(pixelFormat);
    return (formatSearch != PixelConvertAdapter::FFMPEG_PIXEL_FORMAT_MAP.end()) ?
        formatSearch->second : AVPixelFormat::AV_PIX_FMT_NONE;
}

typedef struct FFMpegConvertInfo {
    AVPixelFormat format = AVPixelFormat::AV_PIX_FMT_NONE;
    int32_t width = 0;
    int32_t height = 0;
    int32_t alignSize = 1;
} FFMPEG_CONVERT_INFO;

static bool FFMpegConvert(const void *srcPixels, const FFMPEG_CONVERT_INFO& srcInfo,
    void *dstPixels, const FFMPEG_CONVERT_INFO& dstInfo)
{
    bool ret = true;
    AVFrame *inputFrame = nullptr;
    AVFrame *outputFrame = nullptr;

    if (srcInfo.format == AVPixelFormat::AV_PIX_FMT_NONE ||
        dstInfo.format == AVPixelFormat::AV_PIX_FMT_NONE) {
        IMAGE_LOGE("unsupport src/dst pixel format!");
        return false;
    }
    if (srcInfo.width <= 0 || srcInfo.height <= 0 || dstInfo.width <= 0 || dstInfo.height <= 0) {
        IMAGE_LOGE("src/dst width/height error!");
        return false;
    }

    inputFrame = av_frame_alloc();
    outputFrame = av_frame_alloc();
    if (inputFrame != nullptr && outputFrame != nullptr) {
        struct SwsContext *ctx = sws_getContext(srcInfo.width, srcInfo.height, srcInfo.format,
            dstInfo.width, dstInfo.height, dstInfo.format, SWS_POINT, nullptr, nullptr, nullptr);
        if (ctx != nullptr) {
            av_image_fill_arrays(inputFrame->data, inputFrame->linesize, (uint8_t *)srcPixels,
                srcInfo.format, srcInfo.width, srcInfo.height, srcInfo.alignSize);
            av_image_fill_arrays(outputFrame->data, outputFrame->linesize, (uint8_t *)dstPixels,
                dstInfo.format, dstInfo.width, dstInfo.height, dstInfo.alignSize);

            sws_scale(ctx, (uint8_t const **)inputFrame->data, inputFrame->linesize, 0, srcInfo.height,
                outputFrame->data, outputFrame->linesize);
            sws_freeContext(ctx);
        } else {
            IMAGE_LOGE("FFMpeg: sws_getContext failed!");
            ret = false;
        }
    } else {
        IMAGE_LOGE("FFMpeg: av_frame_alloc failed!");
        ret = false;
    }
    av_frame_free(&inputFrame);
    av_frame_free(&outputFrame);
    return ret;
}

static bool NV12P010ToNV21P010(const uint16_t *srcBuffer, const ImageInfo &info, uint16_t *destBuffer)
{
    const uint16_t *srcUV = srcBuffer + info.size.width * info.size.height;
    uint16_t *dstVU = destBuffer + info.size.width * info.size.height;
    uint32_t sizeUV = info.size.width * info.size.height / NUM_2;
    for (uint32_t i = 0; i < static_cast<uint32_t>(info.size.width * info.size.height); i++) {
        destBuffer[i] = srcBuffer[i];
    }
    for (uint32_t i = 0; i < sizeUV; i += NUM_2) {
        dstVU[i] = srcUV[i + 1];
        dstVU[i + 1] = srcUV[i];
    }
    return true;
}

bool IsYUVP010Format(PixelFormat format)
{
    return (format == PixelFormat::YCBCR_P010) || (format == PixelFormat::YCRCB_P010);
}

static bool ConvertForFFMPEG(const void *srcPixels, PixelFormat srcpixelmap, ImageInfo srcInfo,
    void *dstPixels, PixelFormat dstpixelmap)
{
    FFMPEG_CONVERT_INFO srcFFmpegInfo = {PixelFormatToAVPixelFormat(srcpixelmap),
        srcInfo.size.width, srcInfo.size.height, 1};
    FFMPEG_CONVERT_INFO dstFFmpegInfo = {PixelFormatToAVPixelFormat(dstpixelmap),
        srcInfo.size.width, srcInfo.size.height, 1};
    if (!FFMpegConvert(srcPixels, srcFFmpegInfo, dstPixels, dstFFmpegInfo)) {
        IMAGE_LOGE("[PixelMap]Convert: ffmpeg convert failed!");
        return false;
    }
    return true;
}

// Convert and collapse pixels by removing line paddings if any
static bool ConvertAndCollapseByFFMpeg(const void *srcPixels, const ImageInfo &srcInfo, void *dstPixels,
    const ImageInfo &dstInfo, bool useDMA)
{
    FFMPEG_CONVERT_INFO srcFFMpegInfo = {PixelFormatToAVPixelFormat(srcInfo.pixelFormat),
        srcInfo.size.width, srcInfo.size.height, useDMA ? DMA_LINE_SIZE : 1};
    FFMPEG_CONVERT_INFO dstFFMpegInfo = {PixelFormatToAVPixelFormat(dstInfo.pixelFormat),
        dstInfo.size.width, dstInfo.size.height, 1};
    if (!FFMpegConvert(srcPixels, srcFFMpegInfo, dstPixels, dstFFMpegInfo)) {
        IMAGE_LOGE("[PixelMap] ConvertAndCollapseByFFMpeg: FFMpeg convert failed!");
        return false;
    }
    return true;
}

static bool P010ConvertRGBA1010102(const void *srcPixels, ImageInfo srcInfo,
    void *dstPixels, ImageInfo dstInfo)
{
    FFMPEG_CONVERT_INFO srcFFmpegInfo = {PixelFormatToAVPixelFormat(srcInfo.pixelFormat),
        srcInfo.size.width, srcInfo.size.height, 1};
    FFMPEG_CONVERT_INFO tmpFFmpegInfo = {PixelFormatToAVPixelFormat(PixelFormat::RGBA_F16),
        srcInfo.size.width, srcInfo.size.height, 1};
    int tmpPixelsLen = av_image_get_buffer_size(tmpFFmpegInfo.format, tmpFFmpegInfo.width, tmpFFmpegInfo.height,
        tmpFFmpegInfo.alignSize);
    if (tmpPixelsLen <= 0) {
        IMAGE_LOGE("[PixelMap]Convert: Get tmp pixels length failed!");
        return false;
    }
    uint8_t* tmpPixels = new(std::nothrow) uint8_t[tmpPixelsLen];
    if (tmpPixels == nullptr) {
        IMAGE_LOGE("[PixelMap]Convert: alloc memory failed!");
        return false;
    }
    memset_s(tmpPixels, tmpPixelsLen, 0, tmpPixelsLen);
    if (!FFMpegConvert(srcPixels, srcFFmpegInfo, tmpPixels, tmpFFmpegInfo)) {
        IMAGE_LOGE("[PixelMap]Convert: ffmpeg convert failed!");
        delete[] tmpPixels;
        tmpPixels = nullptr;
        return false;
    }
    ImageInfo tmpInfo = srcInfo;
    tmpInfo.pixelFormat = PixelFormat::RGBA_U16;
    tmpInfo.alphaType = AlphaType::IMAGE_ALPHA_TYPE_PREMUL;
    Position pos;
    if (!PixelConvertAdapter::WritePixelsConvert(tmpPixels, PixelMap::GetRGBxRowDataSize(tmpInfo), tmpInfo,
        dstPixels, pos, PixelMap::GetRGBxRowDataSize(dstInfo), dstInfo)) {
        IMAGE_LOGE("[PixelMap]Convert: ConvertFromYUV: pixel convert in adapter failed.");
        delete[] tmpPixels;
        tmpPixels = nullptr;
        return false;
    }
    delete[] tmpPixels;
    tmpPixels = nullptr;
    return true;
}

static bool ConvertRGBA1010102ToYUV(const void *srcPixels, ImageInfo srcInfo,
    void *dstPixels, ImageInfo dstInfo)
{
    ImageInfo tmpInfo = srcInfo;
    tmpInfo.pixelFormat = PixelFormat::RGBA_U16;
    int tmpPixelsLen = PixelMap::GetRGBxByteCount(tmpInfo);
    if (tmpPixelsLen <= 0) {
        IMAGE_LOGE("[PixelMap]Convert: Get tmp pixels length failed!");
        return false;
    }
    uint8_t* tmpPixels = new(std::nothrow) uint8_t[tmpPixelsLen];
    if (tmpPixels == nullptr) {
        IMAGE_LOGE("[PixelMap]Convert: alloc memory failed!");
        return false;
    }
    memset_s(tmpPixels, tmpPixelsLen, 0, tmpPixelsLen);

    Position pos;
    if (!PixelConvertAdapter::WritePixelsConvert(srcPixels, PixelMap::GetRGBxRowDataSize(srcInfo), srcInfo,
        tmpPixels, pos, PixelMap::GetRGBxRowDataSize(tmpInfo), tmpInfo)) {
        IMAGE_LOGE("[PixelMap]Convert: ConvertToYUV: pixel convert in adapter failed.");
        delete[] tmpPixels;
        tmpPixels = nullptr;
        return false;
    }

    FFMPEG_CONVERT_INFO srcFFmpegInfo = {PixelFormatToAVPixelFormat(PixelFormat::RGBA_F16),
        tmpInfo.size.width, tmpInfo.size.height, 1};
    FFMPEG_CONVERT_INFO dstFFmpegInfo = {PixelFormatToAVPixelFormat(dstInfo.pixelFormat),
        dstInfo.size.width, dstInfo.size.height, 1};
    if (!FFMpegConvert(tmpPixels, srcFFmpegInfo, dstPixels, dstFFmpegInfo)) {
        IMAGE_LOGE("[PixelMap]Convert: ffmpeg convert failed!");
        delete[] tmpPixels;
        tmpPixels = nullptr;
        return false;
    }
    delete[] tmpPixels;
    tmpPixels = nullptr;
    return true;
}

static int32_t YUVConvertRGB(const void *srcPixels, const ImageInfo &srcInfo,
    void *dstPixels, const ImageInfo &dstInfo)
{
    FFMPEG_CONVERT_INFO srcFFmpegInfo = {PixelFormatToAVPixelFormat(srcInfo.pixelFormat),
        srcInfo.size.width, srcInfo.size.height, 1};
    FFMPEG_CONVERT_INFO tmpFFmpegInfo = {PixelFormatToAVPixelFormat(PixelFormat::RGB_888),
        srcInfo.size.width, srcInfo.size.height, 1};
    int tmpPixelsLen = av_image_get_buffer_size(tmpFFmpegInfo.format, tmpFFmpegInfo.width, tmpFFmpegInfo.height,
        tmpFFmpegInfo.alignSize);
    if (tmpPixelsLen <= 0) {
        IMAGE_LOGE("[PixelMap]Convert: Get tmp pixels length failed!");
        return -1;
    }
    uint8_t* tmpPixels = new(std::nothrow) uint8_t[tmpPixelsLen];
    if (tmpPixels == nullptr) {
        IMAGE_LOGE("[PixelMap]Convert: alloc memory failed!");
        return -1;
    }
    memset_s(tmpPixels, tmpPixelsLen, 0, tmpPixelsLen);
    if (!FFMpegConvert(srcPixels, srcFFmpegInfo, reinterpret_cast<void *>(tmpPixels), tmpFFmpegInfo)) {
        IMAGE_LOGE("[PixelMap]Convert: ffmpeg convert failed!");
        delete[] tmpPixels;
        tmpPixels = nullptr;
        return -1;
    }

    ImageInfo tmpInfo = srcInfo;
    tmpInfo.pixelFormat = PixelFormat::RGB_888;
    Position pos;
    if (!PixelConvertAdapter::WritePixelsConvert(tmpPixels, PixelMap::GetRGBxRowDataSize(tmpInfo), tmpInfo,
        dstPixels, pos, PixelMap::GetRGBxRowDataSize(dstInfo), dstInfo)) {
        IMAGE_LOGE("[PixelMap]Convert: ConvertFromYUV: pixel convert in adapter failed.");
        delete[] tmpPixels;
        tmpPixels = nullptr;
        return -1;
    }

    delete[] tmpPixels;
    tmpPixels = nullptr;
    return PixelMap::GetRGBxByteCount(dstInfo);
}

static int32_t ConvertFromYUV(const void *srcPixels, const int32_t srcLength, const ImageInfo &srcInfo,
    void *dstPixels, const ImageInfo &dstInfo)
{
    if (srcPixels == nullptr || dstPixels == nullptr || srcLength <= 0) {
        IMAGE_LOGE("[PixelMap]Convert: src pixels or dst pixels or src pixels length invalid.");
        return -1;
    }
    if ((srcInfo.pixelFormat != PixelFormat::NV21 && srcInfo.pixelFormat != PixelFormat::NV12) ||
        (dstInfo.pixelFormat == PixelFormat::NV21 || dstInfo.pixelFormat == PixelFormat::NV12)) {
        IMAGE_LOGE("[PixelMap]Convert: src or dst pixel format invalid.");
        return -1;
    }
    if ((srcInfo.pixelFormat == PixelFormat::NV12 && dstInfo.pixelFormat == PixelFormat::YCBCR_P010) ||
        (srcInfo.pixelFormat == PixelFormat::NV21 && dstInfo.pixelFormat == PixelFormat::YCRCB_P010)) {
        return ConvertForFFMPEG(srcPixels, PixelFormat::NV12, srcInfo, dstPixels,
            PixelFormat::YCBCR_P010) == true ? PixelMap::GetYUVByteCount(dstInfo) : -1;
    }
    if ((srcInfo.pixelFormat == PixelFormat::NV12 && dstInfo.pixelFormat == PixelFormat::YCRCB_P010) ||
        (srcInfo.pixelFormat == PixelFormat::NV21 && dstInfo.pixelFormat == PixelFormat::YCBCR_P010)) {
        return ConvertForFFMPEG(srcPixels, PixelFormat::NV21, srcInfo, dstPixels,
            PixelFormat::YCBCR_P010) == true ? PixelMap::GetYUVByteCount(dstInfo) : -1;
    }

    return YUVConvertRGB(srcPixels, srcInfo, dstPixels, dstInfo);
}

static int32_t ConvertFromP010(const void *srcPixels, const int32_t srcLength, const ImageInfo &srcInfo,
    void *dstPixels, const ImageInfo &dstInfo)
{
    if (srcPixels == nullptr || dstPixels == nullptr || srcLength <= 0) {
        IMAGE_LOGE("[PixelMap]Convert: src pixels or dst pixels or src pixels length invalid.");
        return -1;
    }
    if ((srcInfo.pixelFormat != PixelFormat::YCRCB_P010 && srcInfo.pixelFormat != PixelFormat::YCBCR_P010) ||
        IsYUVP010Format(dstInfo.pixelFormat)) {
        IMAGE_LOGE("[PixelMap]Convert: src or dst pixel format invalid.");
        return -1;
    }
    uint8_t* srcP010 = new(std::nothrow) uint8_t[srcLength];
    if (srcP010 == nullptr) {
        IMAGE_LOGE("[PixelMap]Convert: alloc memory failed!");
        return -1;
    }
    memset_s(srcP010, srcLength, 0, srcLength);
    if (srcInfo.pixelFormat == PixelFormat::YCRCB_P010) {
        NV12P010ToNV21P010((uint16_t *)srcPixels, srcInfo, (uint16_t *)srcP010);
    } else {
        if (memcpy_s(srcP010, srcLength, srcPixels, srcLength) != 0) {
            delete[] srcP010;
            srcP010 = nullptr;
            return -1;
        }
    }
    if (dstInfo.pixelFormat == PixelFormat::RGBA_1010102) {
        if (P010ConvertRGBA1010102(srcP010, srcInfo, dstPixels, dstInfo)) {
            delete[] srcP010;
            srcP010 = nullptr;
            return PixelMap::GetRGBxByteCount(dstInfo);
        }
        delete[] srcP010;
        srcP010 = nullptr;
        return -1;
    } else {
        if (ConvertForFFMPEG(srcP010, srcInfo.pixelFormat, srcInfo, dstPixels, dstInfo.pixelFormat)) {
            delete[] srcP010;
            srcP010 = nullptr;
            return PixelMap::GetRGBxByteCount(dstInfo);
        }
        delete[] srcP010;
        srcP010 = nullptr;
        return -1;
    }
}

static int32_t RGBConvertYUV(const void *srcPixels, const ImageInfo &srcInfo,
    void *dstPixels, const ImageInfo &dstInfo)
{
    ImageInfo tmpInfo = srcInfo;
    tmpInfo.pixelFormat = PixelFormat::RGB_888;
    int tmpPixelsLen = PixelMap::GetRGBxByteCount(tmpInfo);
    if (tmpPixelsLen <= 0) {
        IMAGE_LOGE("[PixelMap]Convert: Get tmp pixels length failed!");
        return -1;
    }
    uint8_t* tmpPixels = new(std::nothrow) uint8_t[tmpPixelsLen];
    if (tmpPixels == nullptr) {
        IMAGE_LOGE("[PixelMap]Convert: alloc memory failed!");
        return -1;
    }
    memset_s(tmpPixels, tmpPixelsLen, 0, tmpPixelsLen);
    Position pos;
    if (!PixelConvertAdapter::WritePixelsConvert(srcPixels, PixelMap::GetRGBxRowDataSize(srcInfo), srcInfo,
        tmpPixels, pos, PixelMap::GetRGBxRowDataSize(tmpInfo), tmpInfo)) {
        IMAGE_LOGE("[PixelMap]Convert: ConvertToYUV: pixel convert in adapter failed.");
        delete[] tmpPixels;
        tmpPixels = nullptr;
        return -1;
    }
    FFMPEG_CONVERT_INFO srcFFmpegInfo = {PixelFormatToAVPixelFormat(PixelFormat::RGB_888),
        tmpInfo.size.width, tmpInfo.size.height, 1};
    FFMPEG_CONVERT_INFO dstFFmpegInfo = {PixelFormatToAVPixelFormat(dstInfo.pixelFormat),
        dstInfo.size.width, dstInfo.size.height, 1};
    if (!FFMpegConvert(tmpPixels, srcFFmpegInfo, reinterpret_cast<void *>(dstPixels), dstFFmpegInfo)) {
        IMAGE_LOGE("[PixelMap]Convert: ffmpeg convert failed!");
        delete[] tmpPixels;
        tmpPixels = nullptr;
        return -1;
    }
    delete[] tmpPixels;
    tmpPixels = nullptr;
    return av_image_get_buffer_size(dstFFmpegInfo.format, dstFFmpegInfo.width, dstFFmpegInfo.height,
        dstFFmpegInfo.alignSize);
}

static int32_t ConvertToYUV(const void *srcPixels, const int32_t srcLength, const ImageInfo &srcInfo,
    void *dstPixels, const ImageInfo &dstInfo)
{
    if (srcPixels == nullptr || dstPixels == nullptr || srcLength <= 0) {
        IMAGE_LOGE("[PixelMap]Convert: src pixels or dst pixels or src pixel length invalid");
        return -1;
    }
    if ((srcInfo.pixelFormat == PixelFormat::NV21 || srcInfo.pixelFormat == PixelFormat::NV12) ||
        (dstInfo.pixelFormat != PixelFormat::NV21 && dstInfo.pixelFormat != PixelFormat::NV12)) {
        IMAGE_LOGE("[PixelMap]Convert: src or dst pixel format invalid.");
        return -1;
    }
    if ((srcInfo.pixelFormat == PixelFormat::YCBCR_P010 && dstInfo.pixelFormat == PixelFormat::NV12) ||
        (srcInfo.pixelFormat == PixelFormat::YCRCB_P010 && dstInfo.pixelFormat == PixelFormat::NV21)) {
        return ConvertForFFMPEG(srcPixels, PixelFormat::YCBCR_P010, srcInfo, dstPixels,
            PixelFormat::NV12) == true ? PixelMap::GetYUVByteCount(dstInfo) : -1;
    }
    if ((srcInfo.pixelFormat == PixelFormat::YCBCR_P010 && dstInfo.pixelFormat == PixelFormat::NV21) ||
        (srcInfo.pixelFormat == PixelFormat::YCRCB_P010 && dstInfo.pixelFormat == PixelFormat::NV12)) {
        return ConvertForFFMPEG(srcPixels, PixelFormat::YCBCR_P010, srcInfo, dstPixels,
            PixelFormat::NV21) == true ? PixelMap::GetYUVByteCount(dstInfo) : -1;
    }
    return RGBConvertYUV(srcPixels, srcInfo, dstPixels, dstInfo);
}

static int32_t ConvertToP010(const void *srcPixels, const int32_t srcLength, const ImageInfo &srcInfo,
    void *dstPixels, const ImageInfo &dstInfo)
{
    if (srcPixels == nullptr || dstPixels == nullptr || srcLength <= 0) {
        IMAGE_LOGE("[PixelMap]Convert: src pixels or dst pixels or src pixel length invalid");
        return -1;
    }
    if (IsYUVP010Format(srcInfo.pixelFormat) ||
        (dstInfo.pixelFormat != PixelFormat::YCRCB_P010 && dstInfo.pixelFormat != PixelFormat::YCBCR_P010)) {
        IMAGE_LOGE("[PixelMap]Convert: src or dst pixel format invalid.");
        return -1;
    }
    int32_t dstLength = PixelMap::GetYUVByteCount(dstInfo);
    if (dstLength <= 0) {
        IMAGE_LOGE("[PixelMap]Convert: Get dstP010 length failed!");
        return -1;
    }
    uint8_t* dstP010 = new(std::nothrow) uint8_t[dstLength];
    if (dstP010 == nullptr) {
        IMAGE_LOGE("[PixelMap]Convert: alloc memory failed!");
        return -1;
    }
    memset_s(dstP010, dstLength, 0, dstLength);

    if (srcInfo.pixelFormat == PixelFormat::RGBA_1010102) {
        if (!ConvertRGBA1010102ToYUV(srcPixels, srcInfo, dstP010, dstInfo)) {
            delete[] dstP010;
            dstP010 = nullptr;
            return -1;
        }
    } else {
        if (!ConvertForFFMPEG(srcPixels, srcInfo.pixelFormat, srcInfo, dstP010, dstInfo.pixelFormat)) {
            delete[] dstP010;
            dstP010 = nullptr;
            return -1;
        }
    }
    if (dstInfo.pixelFormat == PixelFormat::YCRCB_P010) {
        NV12P010ToNV21P010((uint16_t *)dstP010, dstInfo, (uint16_t *)dstPixels);
    } else {
        if (memcpy_s(dstPixels, dstLength, dstP010, dstLength) != 0) {
            delete[] dstP010;
            dstP010 = nullptr;
            return -1;
        }
    }
    delete[] dstP010;
    dstP010 = nullptr;
    return dstLength;
}

static int32_t YUVConvert(const void *srcPixels, const int32_t srcLength, const ImageInfo &srcInfo,
    void *dstPixels, const ImageInfo &dstInfo)
{
    if (srcInfo.pixelFormat == dstInfo.pixelFormat &&
        srcInfo.size.width == dstInfo.size.width && srcInfo.size.height == dstInfo.size.height) {
            IMAGE_LOGE("src pixel format is equal dst pixel format. no need to convert.");
            auto result = memcpy_s(dstPixels, srcLength, srcPixels, srcLength);
            return result == 0 ? srcLength : -1;
    }
    if (IsYUVP010Format(srcInfo.pixelFormat) && IsYUVP010Format(dstInfo.pixelFormat)) {
        if (srcInfo.size.width == dstInfo.size.width && srcInfo.size.height == dstInfo.size.height) {
            return NV12P010ToNV21P010((uint16_t *)srcPixels, dstInfo, (uint16_t *)dstPixels) == true ?
                srcLength : -1;
        }
    }
    FFMPEG_CONVERT_INFO srcFFmpegInfo = {PixelFormatToAVPixelFormat(srcInfo.pixelFormat), srcInfo.size.width,
        srcInfo.size.height, 1};
    FFMPEG_CONVERT_INFO dstFFmpegInfo = {PixelFormatToAVPixelFormat(dstInfo.pixelFormat), dstInfo.size.width,
        dstInfo.size.height, 1};
    if (!FFMpegConvert(srcPixels, srcFFmpegInfo, dstPixels, dstFFmpegInfo)) {
        IMAGE_LOGE("[PixelMap]Convert: ffmpeg convert failed!");
        return -1;
    }
    return av_image_get_buffer_size(dstFFmpegInfo.format, dstFFmpegInfo.width, dstFFmpegInfo.height,
        dstFFmpegInfo.alignSize);
}

static bool IsInterYUVConvert(PixelFormat srcPixelFormat, PixelFormat dstPixelFormat)
{
    return (srcPixelFormat == PixelFormat::NV12 || srcPixelFormat == PixelFormat::NV21) &&
        (dstPixelFormat == PixelFormat::NV12 || dstPixelFormat == PixelFormat::NV21);
}

int32_t PixelConvert::PixelsConvert(const BufferInfo &srcInfo, BufferInfo &dstInfo, int32_t srcLength, bool useDMA)
{
    if (!IsValidBufferInfo(srcInfo) || !IsValidBufferInfo(dstInfo) || srcLength <= 0) {
        IMAGE_LOGE("[PixelMap]Convert: pixels or image info or row stride or src pixels length invalid.");
        return -1;
    }

    const ImageInfo srcImageInfo = srcInfo.imageInfo;
    const ImageInfo dstImageInfo = dstInfo.imageInfo;
    if (dstImageInfo.pixelFormat == PixelFormat::ARGB_8888) {
        return ConvertAndCollapseByFFMpeg(srcInfo.pixels, srcImageInfo, dstInfo.pixels, dstImageInfo, useDMA) ?
            PixelMap::GetRGBxByteCount(dstImageInfo) : -1;
    }
    if (IsInterYUVConvert(srcImageInfo.pixelFormat, dstImageInfo.pixelFormat) ||
        (IsYUVP010Format(srcImageInfo.pixelFormat) && IsYUVP010Format(dstImageInfo.pixelFormat))) {
        return YUVConvert(srcInfo.pixels, srcLength, srcImageInfo, dstInfo.pixels, dstImageInfo);
    }
    if (srcImageInfo.pixelFormat == PixelFormat::NV12 || srcImageInfo.pixelFormat == PixelFormat::NV21) {
        return ConvertFromYUV(srcInfo.pixels, srcLength, srcImageInfo, dstInfo.pixels, dstImageInfo);
    } else if (dstImageInfo.pixelFormat == PixelFormat::NV12 || dstImageInfo.pixelFormat == PixelFormat::NV21) {
        return ConvertToYUV(srcInfo.pixels, srcLength, srcImageInfo, dstInfo.pixels, dstImageInfo);
    } else if (IsYUVP010Format(srcImageInfo.pixelFormat)) {
        return ConvertFromP010(srcInfo.pixels, srcLength, srcImageInfo, dstInfo.pixels, dstImageInfo);
    } else if (IsYUVP010Format(dstImageInfo.pixelFormat)) {
        return ConvertToP010(srcInfo.pixels, srcLength, srcImageInfo, dstInfo.pixels, dstImageInfo);
    }

    Position pos;
    if (!PixelConvertAdapter::WritePixelsConvert(srcInfo.pixels,
        srcInfo.rowStride == 0 ? PixelMap::GetRGBxRowDataSize(srcImageInfo) : srcInfo.rowStride, srcImageInfo,
        dstInfo.pixels, pos, useDMA ? dstInfo.rowStride : PixelMap::GetRGBxRowDataSize(dstImageInfo), dstImageInfo)) {
        IMAGE_LOGE("[PixelMap]Convert: PixelsConvert: pixel convert in adapter failed.");
        return -1;
    }

    return PixelMap::GetRGBxByteCount(dstImageInfo);
}

PixelConvert::PixelConvert(ProcFuncType funcPtr, ProcFuncExtension extension, bool isNeedConvert)
    : procFunc_(funcPtr), procFuncExtension_(extension), isNeedConvert_(isNeedConvert)
{}

// caller need setting the correct pixelFormat and alphaType
std::unique_ptr<PixelConvert> PixelConvert::Create(const ImageInfo &srcInfo, const ImageInfo &dstInfo)
{
    if (srcInfo.pixelFormat == PixelFormat::UNKNOWN || dstInfo.pixelFormat == PixelFormat::UNKNOWN) {
        IMAGE_LOGE("source or destination pixel format unknown");
        return nullptr;
    }
    uint32_t srcFormat = static_cast<uint32_t>(srcInfo.pixelFormat);
    uint32_t dstFormat = static_cast<uint32_t>(dstInfo.pixelFormat);
    ProcFuncType funcPtr = GetProcFuncType(srcFormat, dstFormat);
    if (funcPtr == nullptr) {
        IMAGE_LOGE("not found convert function. pixelFormat %{public}u -> %{public}u", srcFormat, dstFormat);
        return nullptr;
    }
    ProcFuncExtension extension;
    extension.alphaConvertType = GetAlphaConvertType(srcInfo.alphaType, dstInfo.alphaType);
    bool isNeedConvert = true;
    if ((srcInfo.pixelFormat == dstInfo.pixelFormat) && (extension.alphaConvertType == AlphaConvertType::NO_CONVERT)) {
        isNeedConvert = false;
    }
    return make_unique<PixelConvert>(funcPtr, extension, isNeedConvert);
}

AlphaConvertType PixelConvert::GetAlphaConvertType(const AlphaType &srcType, const AlphaType &dstType)
{
    if (srcType == AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN || dstType == AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN) {
        IMAGE_LOGD("source or destination alpha type unknown");
        return AlphaConvertType::NO_CONVERT;
    }
    if ((srcType == AlphaType::IMAGE_ALPHA_TYPE_PREMUL) && (dstType == AlphaType::IMAGE_ALPHA_TYPE_UNPREMUL)) {
        return AlphaConvertType::PREMUL_CONVERT_UNPREMUL;
    }
    if ((srcType == AlphaType::IMAGE_ALPHA_TYPE_PREMUL) && (dstType == AlphaType::IMAGE_ALPHA_TYPE_OPAQUE)) {
        return AlphaConvertType::PREMUL_CONVERT_OPAQUE;
    }
    if ((srcType == AlphaType::IMAGE_ALPHA_TYPE_UNPREMUL) && (dstType == AlphaType::IMAGE_ALPHA_TYPE_PREMUL)) {
        return AlphaConvertType::UNPREMUL_CONVERT_PREMUL;
    }
    if ((srcType == AlphaType::IMAGE_ALPHA_TYPE_UNPREMUL) && (dstType == AlphaType::IMAGE_ALPHA_TYPE_OPAQUE)) {
        return AlphaConvertType::UNPREMUL_CONVERT_OPAQUE;
    }
    return AlphaConvertType::NO_CONVERT;
}

bool PixelConvert::IsValidRowStride(int32_t rowStride, const ImageInfo &imageInfo)
{
    if (imageInfo.pixelFormat == PixelFormat::YCBCR_P010 ||
        imageInfo.pixelFormat == PixelFormat::YCRCB_P010) {
        return rowStride == 0 || rowStride >= imageInfo.size.width * YUV420_P010_BYTES;
    }
    return rowStride == 0 || rowStride >= imageInfo.size.width * ImageUtils::GetPixelBytes(imageInfo.pixelFormat);
}

bool PixelConvert::IsValidBufferInfo(const BufferInfo &bufferInfo)
{
    return bufferInfo.pixels != nullptr && IsValidRowStride(bufferInfo.rowStride, bufferInfo.imageInfo);
}

void PixelConvert::Convert(void *destinationPixels, const uint8_t *sourcePixels, uint32_t sourcePixelsNum)
{
    if ((destinationPixels == nullptr) || (sourcePixels == nullptr)) {
        IMAGE_LOGE("destinationPixel or sourcePixel is null");
        return;
    }
    if (!isNeedConvert_) {
        IMAGE_LOGD("no need convert");
        return;
    }
    procFunc_(destinationPixels, sourcePixels, sourcePixelsNum, procFuncExtension_);
}
} // namespace Media
} // namespace OHOS