/****************************************************************************
*
*    Copyright 2012 - 2023 Vivante Corporation, Santa Clara, California.
*    All Rights Reserved.
*
*    Copyright 2024 NXP
*
*    Permission is hereby granted, free of charge, to any person obtaining
*    a copy of this software and associated documentation files (the
*    'Software'), to deal in the Software without restriction, including
*    without limitation the rights to use, copy, modify, merge, publish,
*    distribute, sub license, and/or sell copies of the Software, and to
*    permit persons to whom the Software is furnished to do so, subject
*    to the following conditions:
*
*    The above copyright notice and this permission notice (including the
*    next paragraph) shall be included in all copies or substantial
*    portions of the Software.
*
*    THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
*    IN NO EVENT SHALL VIVANTE AND/OR ITS SUPPLIERS BE LIABLE FOR ANY
*    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
*    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
*    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*****************************************************************************/

#include "vg_lite_context.h"

#define MATRIX_FP_ABS(x)            (((x) < 0) ? -(x) : (x))
#define MATRIX_FP_EPS               2.2204460492503131e-14

extern vg_lite_matrix_t identity_mtx;

/* Get the plane memory pointer and strides info. */
static uint32_t get_buffer_planes(vg_lite_buffer_t *buffer,
                              uint8_t **memory,
                              uint32_t *strides)
{
    uint32_t count = 1;
    
    switch (buffer->format) {
        case VG_LITE_RGBA8888:
        case VG_LITE_BGRA8888:
        case VG_LITE_RGBX8888:
        case VG_LITE_BGRX8888:
        case VG_LITE_RGB565:
        case VG_LITE_BGR565:
        case VG_LITE_RGBA4444:
        case VG_LITE_BGRA4444:
        case VG_LITE_BGRA5551:
        case VG_LITE_A8:
        case VG_LITE_L8:
        case VG_LITE_A4:
        case VG_LITE_INDEX_1:
        case VG_LITE_INDEX_2:
        case VG_LITE_INDEX_4:
        case VG_LITE_INDEX_8:
        case VG_LITE_YUYV:
        case VG_LITE_YUY2:
        case VG_LITE_RGBA2222:
            count = 1;
            memory[0] = (uint8_t *)buffer->memory;
            memory[1] = memory[2] = ((uint8_t*)0);
            strides[0] = buffer->stride;
            strides[1] = strides[2] = 0;
            break;

        case VG_LITE_NV12:
        case VG_LITE_NV16:
        case VG_LITE_NV24:
        case VG_LITE_NV24_TILED:
            count = 2;
            memory[0] = (uint8_t *)buffer->memory;
            memory[1] = (uint8_t *)buffer->yuv.uv_memory;
            memory[2] = 0;
            strides[0] = buffer->stride;
            strides[1] = buffer->yuv.uv_stride;
            strides[2] = 0;
            break;
            
        case VG_LITE_AYUY2:
            count = 2;
            memory[0] = (uint8_t *)buffer->memory;
            memory[1] = 0;
            memory[2] = (uint8_t *)buffer->yuv.v_memory;
            strides[0] = buffer->stride;
            strides[1] = 0;
            strides[2] = buffer->yuv.alpha_stride;
            break;

        case VG_LITE_ANV12:
            count = 3;
            memory[0] = (uint8_t *)buffer->memory;
            memory[1] = (uint8_t *)buffer->yuv.uv_memory;
            memory[2] = (uint8_t *)buffer->yuv.v_memory;
            strides[0] = buffer->stride;
            strides[1] = buffer->yuv.uv_stride;
            strides[2] = buffer->yuv.alpha_stride;
            break;
            
        case VG_LITE_YV12:
        case VG_LITE_YV24:
        case VG_LITE_YV16:
            count = 3;
            memory[0] = (uint8_t *)buffer->memory;
            memory[1] = (uint8_t *)buffer->yuv.uv_memory;
            memory[2] = (uint8_t *)buffer->yuv.v_memory;
            strides[0] = buffer->stride;
            strides[1] = buffer->yuv.uv_stride;
            strides[2] = buffer->yuv.v_stride;
            break;
            
        case VG_LITE_YUY2_TILED:
        case VG_LITE_NV12_TILED:
        case VG_LITE_ANV12_TILED:
        case VG_LITE_AYUY2_TILED:
        default:
            count = 0;
            
            break;
    }
    return count;
}

vg_lite_error_t vg_lite_upload_buffer(vg_lite_buffer_t  *buffer,
                                      vg_lite_uint8_t *data[3],
                                      vg_lite_uint32_t stride[3])
{
#if DUMP_API
    FUNC_DUMP(vg_lite_upload_buffer)(buffer, data, stride);
#endif

    vg_lite_error_t error = VG_LITE_SUCCESS;
    int32_t plane_count;
    uint8_t  *buffer_memory[3] = {((uint8_t*)0)};
    uint32_t  buffer_strides[3] = {0};
    uint8_t  *pdata;
    int32_t i, j;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_upload_buffer %p %p %p\n", buffer, data, stride);
#endif

    /* Get buffer memory info. */
    plane_count = get_buffer_planes(buffer, buffer_memory, buffer_strides);

    if (plane_count > 0 && plane_count <= 3) {
        /* Copy the data to buffer. */
        for (i = 0; i < plane_count;  i++) {
            pdata = data[i];
            for (j = 0; j < buffer->height; j++) {
                memcpy(buffer_memory[i], pdata, buffer_strides[i]);
                buffer_memory[i] += buffer_strides[i];
                pdata += stride[i];
            }
        }
    }
    else {
        error = VG_LITE_INVALID_ARGUMENT;
    }

    return error;
}

static vg_lite_error_t swap(float* a, float* b)
{
    float temp;
    if (a == NULL || b == NULL)
        return VG_LITE_INVALID_ARGUMENT;
    temp = *a;
    *a = *b;
    *b = temp;
    return VG_LITE_SUCCESS;
}

vg_lite_error_t vg_lite_get_transform_matrix(vg_lite_float_point4_t src, vg_lite_float_point4_t dst, vg_lite_matrix_t* mat)
{
    float a[8][8], b[9], A[64];
    int i, j, k, m = 8, n = 1;
    int astep = 8, bstep = 1;
    float d;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_get_transform_matrix %p %p %p\n", src, dst, mat);
#endif

    if (src == NULL || dst == NULL || mat == NULL)
        return  VG_LITE_INVALID_ARGUMENT;

    for (i = 0; i < 4; ++i)
    {
        a[i][0] = a[i + 4][3] = (float)src[i].x;
        a[i][1] = a[i + 4][4] = (float)src[i].y;
        a[i][2] = a[i + 4][5] = 1.0f;
        a[i][3] = a[i][4] = a[i][5] =
            a[i + 4][0] = a[i + 4][1] = a[i + 4][2] = 0.0f;
        a[i][6] = (float)(-src[i].x * dst[i].x);
        a[i][7] = (float)(-src[i].y * dst[i].x);
        a[i + 4][6] = (float)(-src[i].x * dst[i].y);
        a[i + 4][7] = (float)(-src[i].y * dst[i].y);
        b[i] = (float)dst[i].x;
        b[i + 4] = (float)dst[i].y;
    }
    for (i = 0; i < 8; ++i)
    {
        for (j = 0; j < 8; ++j)
        {
            A[8 * i + j] = a[i][j];
        }
    }

    for (i = 0; i < m; i++)
    {
        k = i;
        for (j = i + 1; j < m; j++)
            if (MATRIX_FP_ABS(A[j * astep + i]) > MATRIX_FP_ABS(A[k * astep + i]))
                k = j;
        if (MATRIX_FP_ABS((double)A[k * astep + i]) < MATRIX_FP_EPS)
            return VG_LITE_INVALID_ARGUMENT;
        if (k != i)
        {
            for (j = i; j < m; j++)
                swap(&A[i * astep + j], &A[k * astep + j]);
            for (j = 0; j < n; j++)
                swap(&b[i * bstep + j], &b[k * bstep + j]);
        }
        d = -1 / A[i * astep + i];
        for (j = i + 1; j < m; j++)
        {
            float alpha = A[j * astep + i] * d;
            for (k = i + 1; k < m; k++)
                A[j * astep + k] += alpha * A[i * astep + k];
            for (k = 0; k < n; k++)
                b[j * bstep + k] += alpha * b[i * bstep + k];
        }
    }

    for (i = m - 1; i >= 0; i--)
        for (j = 0; j < n; j++)
        {
            float s = b[i * bstep + j];
            for (k = i + 1; k < m; k++)
                s -= A[i * astep + k] * b[k * bstep + j];
            b[i * bstep + j] = s / A[i * astep + i];
        }

    b[8] = 1;

    for (i = 0; i < 3; ++i)
    {
        for (j = 0; j < 3; ++j)
        {
            mat->m[i][j] = b[i * 3 + j];
        }
    }

    return VG_LITE_SUCCESS;
}

vg_lite_error_t vg_lite_set_scissor(vg_lite_int32_t x, vg_lite_int32_t y, vg_lite_int32_t right, vg_lite_int32_t bottom)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_set_scissor)(x, y, right, bottom);
#endif

#if gcFEATURE_VG_SCISSOR
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_set_scissor %d %d %d %d\n", x, y, right, bottom);
#endif

    /* Save scissor Box States. */
    s_context.scissor[0] = x;
    s_context.scissor[1] = y;
    s_context.scissor[2] = right;
    s_context.scissor[3] = bottom;

    /* Scissor dirty. */
    s_context.scissor_dirty = 1;
    s_context.scissor_set = 1;

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_enable_scissor()
{
#if DUMP_API
    FUNC_DUMP(vg_lite_enable_scissor)();
#endif

#if gcFEATURE_VG_MASK

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_enable_scissor\n");
#endif

    /* Enable scissor Mode. */
    if (!s_context.scissor_enable) {
        s_context.scissor_enable = 1 << 4;
        s_context.scissor_dirty = 1;
    }

    return VG_LITE_SUCCESS;
#else
    /* Noop */
    return VG_LITE_SUCCESS;
#endif
}

vg_lite_error_t vg_lite_disable_scissor()
{
#if DUMP_API
    FUNC_DUMP(vg_lite_disable_scissor)();
#endif

#if gcFEATURE_VG_MASK

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_disable_scissor\n");
#endif

    /* Disable scissor Mode. */
    if (s_context.scissor_enable) {
        s_context.scissor_enable = 0;
        s_context.scissor_dirty = 1;
    }

    return VG_LITE_SUCCESS;
#else
    /* Noop */
    return VG_LITE_SUCCESS;
#endif
}

vg_lite_error_t vg_lite_set_CLUT(vg_lite_uint32_t count, vg_lite_uint32_t* colors)
{
#if gcFEATURE_VG_IM_INDEX_FORMAT
    vg_lite_error_t error = VG_LITE_SUCCESS;
    uint32_t addr = 0x0B00;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_set_CLUT %d %p\n", count, colors);
#endif

#if gcFEATURE_VG_NEW_IMAGE_INDEX
    {
        switch (count) {
        case 256:
        case 16:
        case 4:
        case 2:
            addr = 0x0B00;
            break;
        default:
            error = VG_LITE_INVALID_ARGUMENT;
            return error;
            break;
        }
    }
#else
    {
        switch (count) {
        case 256:
            addr = 0x0B00;
            break;
        case 16:
            addr = 0x0AA0;
            break;
        case 4:
            addr = 0x0A9C;
            break;
        case 2:
            addr = 0x0A98;
            break;
        default:
            error = VG_LITE_INVALID_ARGUMENT;
            return error;
            break;
        }
    }
#endif

    VG_LITE_RETURN_ERROR(push_clut(&s_context, addr, count, (uint32_t*)colors));

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_source_global_alpha(vg_lite_global_alpha_t alpha_mode, vg_lite_uint8_t alpha_value)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_source_global_alpha)(alpha_mode, alpha_value);
#endif

#if gcFEATURE_VG_GLOBAL_ALPHA
    uint32_t image_alpha_mode;
    uint32_t image_alpha_value;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_source_global_alpha %d %d\n", alpha_mode, alpha_value);
#endif

    image_alpha_mode = (uint8_t)alpha_mode;
    image_alpha_value = alpha_value << 2;

    s_context.src_alpha_mode = image_alpha_mode;
    s_context.src_alpha_value = image_alpha_value;

    return VG_LITE_SUCCESS;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_dest_global_alpha(vg_lite_global_alpha_t alpha_mode, vg_lite_uint8_t alpha_value)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_dest_global_alpha)(alpha_mode, alpha_value);
#endif

#if gcFEATURE_VG_GLOBAL_ALPHA
    uint32_t dest_alpha_mode;
    uint32_t dest_alpha_value;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_dest_global_alpha %d %d\n", alpha_mode, alpha_value);
#endif

    dest_alpha_mode = (alpha_mode == VG_LITE_NORMAL) ? 0 : (alpha_mode == VG_LITE_GLOBAL) ? 0x00000400 : 0x00000800;
    dest_alpha_value = alpha_value << 12;

    s_context.dst_alpha_mode = dest_alpha_mode;
    s_context.dst_alpha_value = dest_alpha_value;

    return VG_LITE_SUCCESS;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_set_color_key(vg_lite_color_key4_t colorkey)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_set_color_key)(colorkey);
#endif

#if gcFEATURE_VG_COLOR_KEY
    uint8_t i;
    uint32_t value_low = 0;
    uint32_t value_high = 0;
    uint8_t r, g, b, a, e;
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_set_color_key %p\n", colorkey);
#endif

    /* Set color key states. */
    for (i = 0; i < 4; i++)
    {
        /* Set gcregVGPEColorKeyLow. Layout "E/R/G/B". */
        r = colorkey[i].low_r;
        g = colorkey[i].low_g;
        b = colorkey[i].low_b;
        e = colorkey[i].enable;
        value_low = (e << 24) | (r << 16) | (g << 8) | b;
        VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A90 + i, value_low));

        /* Set gcregVGPEColorKeyHigh. Layout "A/R/G/B". */
        r = colorkey[i].hign_r;
        g = colorkey[i].hign_g;
        b = colorkey[i].hign_b;
        a = colorkey[i].alpha;
        value_high = (a << 24) | (r << 16) | (g << 8) | b;
        VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A94 + i, value_high));
    }

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_enable_dither()
{
#if DUMP_API
    FUNC_DUMP(vg_lite_enable_dither)();
#endif

#if gcFEATURE_VG_DITHER
    vg_lite_error_t error = VG_LITE_SUCCESS;
    uint32_t table_low = 0x7B48F3C0;
    uint32_t table_high = 0x596AD1E2;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_enable_dither\n");
#endif

    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A5A, table_low));
    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A5B, table_high));

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_disable_dither()
{
#if DUMP_API
    FUNC_DUMP(vg_lite_disable_dither)();
#endif

#if gcFEATURE_VG_DITHER
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_disable_dither\n");
#endif

    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A5A, 0xFFFFFFFF));
    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A5B, 0xFFFFFFFF));

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_enable_masklayer()
{
#if DUMP_API
    FUNC_DUMP(vg_lite_enable_masklayer)();
#endif

#if gcFEATURE_VG_MASK
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_enable_masklayer\n");
#endif

    s_context.enable_mask = (1 << 20);

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_disable_masklayer()
{
#if DUMP_API
    FUNC_DUMP(vg_lite_disable_masklayer)();
#endif

#if gcFEATURE_VG_MASK
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_disable_masklayer\n");
#endif

    s_context.enable_mask = 0;

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_create_masklayer(vg_lite_buffer_t* masklayer, vg_lite_uint32_t width, vg_lite_uint32_t height)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_create_masklayer)(masklayer, width, height);
#endif

#if gcFEATURE_VG_MASK
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_create_masklayer %p %d %d\n", masklayer, width, height);
#endif

    memset(masklayer, 0, sizeof(vg_lite_buffer_t));
    masklayer->width = width;
    masklayer->height = height;
    masklayer->format = VG_LITE_A8;
    VG_LITE_RETURN_ERROR(vg_lite_allocate(masklayer));

    VG_LITE_RETURN_ERROR(vg_lite_clear(masklayer, NULL, (vg_lite_color_t)(0xFF << 24)));

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_fill_masklayer(vg_lite_buffer_t* masklayer, vg_lite_rectangle_t* rect, vg_lite_uint8_t value)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_fill_masklayer)(masklayer, rect, value);
#endif

#if gcFEATURE_VG_MASK
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_fill_masklayer %p %p %d\n", masklayer, rect, value);
#endif

    error = vg_lite_clear(masklayer, rect, value << 24);

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_blend_masklayer(
    vg_lite_buffer_t* dst_masklayer,
    vg_lite_buffer_t* src_masklayer,
    vg_lite_mask_operation_t operation,
    vg_lite_rectangle_t* rect
)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_blend_masklayer)(dst_masklayer, src_masklayer, operation, rect);
#endif

#if gcFEATURE_VG_MASK
    vg_lite_error_t error = VG_LITE_SUCCESS;
    vg_lite_matrix_t matrix;
    vg_lite_filter_t filter = VG_LITE_FILTER_POINT;
    vg_lite_rectangle_t area = *rect;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_blend_masklayer %p %p %d %p\n", dst_masklayer, src_masklayer, operation, rect);
#endif

    vg_lite_identity(&matrix);
    vg_lite_translate((vg_lite_float_t)rect->x, (vg_lite_float_t)rect->y, &matrix);

    switch (operation)
    {
    case VG_LITE_CLEAR_MASK:
        VG_LITE_RETURN_ERROR(vg_lite_clear(dst_masklayer, &area, 0x0));
        break;
    case VG_LITE_FILL_MASK:
        VG_LITE_RETURN_ERROR(vg_lite_clear(dst_masklayer, &area, (vg_lite_color_t)(0xFF << 24)));
        break;
    case VG_LITE_SET_MASK:
        area.x = 0;
        area.y = 0;
        VG_LITE_RETURN_ERROR(vg_lite_blit_rect(dst_masklayer, src_masklayer, &area, &matrix, VG_LITE_BLEND_NONE, 0, filter));
        break;
    case VG_LITE_UNION_MASK:
        area.x = 0;
        area.y = 0;
        VG_LITE_RETURN_ERROR(vg_lite_blit_rect(dst_masklayer, src_masklayer, &area, &matrix, VG_LITE_BLEND_SCREEN, 0, filter));
        break;
    case VG_LITE_INTERSECT_MASK:
        area.x = 0;
        area.y = 0;
        VG_LITE_RETURN_ERROR(vg_lite_blit_rect(dst_masklayer, src_masklayer, &area, &matrix, VG_LITE_BLEND_DST_IN, 0, filter));
        break;
    case VG_LITE_SUBTRACT_MASK:
        area.x = 0;
        area.y = 0;
        VG_LITE_RETURN_ERROR(vg_lite_blit_rect(dst_masklayer, src_masklayer, &area, &matrix, VG_LITE_BLEND_SUBTRACT, 0, filter));
        break;
    default:
        break;
    }

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_set_masklayer(vg_lite_buffer_t* masklayer)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_set_masklayer)(masklayer);
#endif

#if gcFEATURE_VG_MASK
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_set_masklayer %p\n", masklayer);
#endif

    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A14, masklayer->address));
    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A15, masklayer->stride));
    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A1B, 0x00000010));

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_render_masklayer(
    vg_lite_buffer_t* masklayer,
    vg_lite_mask_operation_t operation,
    vg_lite_path_t* path,
    vg_lite_fill_t fill_rule,
    vg_lite_color_t color,
    vg_lite_matrix_t* matrix
)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_render_masklayer)(masklayer, operation, path, fill_rule, color, matrix);
#endif

#if gcFEATURE_VG_MASK
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_render_masklayer %p %d %p %d %d %p\n", masklayer, operation, path, fill_rule, color, matrix);
#endif

    if (!matrix) {
        matrix = &identity_mtx;
    }

    switch (operation)
    {
    case VG_LITE_CLEAR_MASK:
        VG_LITE_RETURN_ERROR(vg_lite_draw(masklayer, path, fill_rule, matrix, VG_LITE_BLEND_NONE, 0));
        break;
    case VG_LITE_FILL_MASK:
        VG_LITE_RETURN_ERROR(vg_lite_draw(masklayer, path, fill_rule, matrix, VG_LITE_BLEND_NONE, (vg_lite_color_t)(0xFF << 24)));
        break;
    case VG_LITE_SET_MASK:
        VG_LITE_RETURN_ERROR(vg_lite_draw(masklayer, path, fill_rule, matrix, VG_LITE_BLEND_NONE, color << 24));
        break;
    case VG_LITE_UNION_MASK:
        VG_LITE_RETURN_ERROR(vg_lite_draw(masklayer, path, fill_rule, matrix, VG_LITE_BLEND_SCREEN, color << 24));
        break;
    case VG_LITE_INTERSECT_MASK:
        VG_LITE_RETURN_ERROR(vg_lite_draw(masklayer, path, fill_rule, matrix, VG_LITE_BLEND_DST_IN, color << 24));
        break;
    case VG_LITE_SUBTRACT_MASK:
        VG_LITE_RETURN_ERROR(vg_lite_draw(masklayer, path, fill_rule, matrix, VG_LITE_BLEND_SUBTRACT, color << 24));
        break;
    default:
        break;
    }

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_destroy_masklayer(vg_lite_buffer_t* masklayer)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_destroy_masklayer)(masklayer);
#endif

#if gcFEATURE_VG_MASK
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_destroy_masklayer %p\n", masklayer);
#endif

    VG_LITE_RETURN_ERROR(vg_lite_free(masklayer));

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_set_pixel_matrix(vg_lite_pixel_matrix_t matrix, vg_lite_pixel_channel_enable_t* channel)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_set_pixel_matrix)(matrix, channel);
#endif

#if gcFEATURE_VG_PIXEL_MATRIX
    vg_lite_error_t error = VG_LITE_SUCCESS;
    short pix_matrix[20] = { 0 };
    int i = 0;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_set_pixel_matrix %p (%d %d %d %d)\n", matrix, channel->enable_a, channel->enable_b, channel->enable_g, channel->enable_r);
#endif

    s_context.matrix_enable = (channel->enable_a ? (1 << 17) : 0) |
        (channel->enable_r ? (1 << 23) : 0) |
        (channel->enable_g ? (1 << 22) : 0) |
        (channel->enable_b ? (1 << 21) : 0);

    if (s_context.matrix_enable)
    {
        for (i = 0; i < 20; i++) {
            if (matrix[i] > 127.0f || matrix[i] < -128.0f) {
                return VG_LITE_INVALID_ARGUMENT;
            }
            pix_matrix[i] = (short)(matrix[i] * 256);
            VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0ADE + i, pix_matrix[i]));
        }
    }

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_gaussian_filter(vg_lite_float_t w0, vg_lite_float_t w1, vg_lite_float_t w2)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_gaussian_filter)(w0, w1, w2);
#endif

#if gcFEATURE_VG_GAUSSIAN_BLUR
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_gaussian_filter %f %f %f\n", w0, w1, w2);
#endif

    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0AD2 + 1, (uint32_t)(w0 * 256)));
    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0AD4 + 1, (uint32_t)(w1 * 256)));
    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0AD6 + 1, (uint32_t)(w2 * 256)));

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_scissor_rects(vg_lite_buffer_t *target, vg_lite_uint32_t nums, vg_lite_rectangle_t rect[])
{
#if DUMP_API
    FUNC_DUMP(vg_lite_scissor_rects)(target, nums, rect);
#endif

#if gcFEATURE_VG_MASK
    vg_lite_error_t error = VG_LITE_SUCCESS;
    vg_lite_rectangle_t rect_clamp, rect_draw;
    vg_lite_int32_t left_x, right_x, left_len, middle_len, right_len, stride, j, max_x, max_y;
    vg_lite_uint8_t alpha;
    vg_lite_uint32_t i;
#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_scissor_rects %d %p\n", nums, rect);
    for (i = 0; i < nums; i++) {
        VGLITE_LOG("    Rect(%d, %d, %d, %d)\n", rect[i].x, rect[i].y, rect[i].width, rect[i].height);
    }
#endif

    /* Record scissor enable flag and disable scissor. */
    vg_lite_uint8_t enable = s_context.scissor_enable;
    s_context.scissor_enable = 0;

    /* Free the old scissor layer if its size is too small for target */
    if (s_context.scissor_layer &&
       (s_context.scissor_layer->width < ((target->width + 7) / 8) || s_context.scissor_layer->height < target->height))
    {
        vg_lite_free(s_context.scissor_layer);
        vg_lite_os_free(s_context.scissor_layer);
        s_context.scissor_layer = NULL;
    }

    /* Allocate if scissor layer is NULL */
    if (s_context.scissor_layer == NULL) {
        s_context.scissor_layer = (vg_lite_buffer_t *)vg_lite_os_malloc(sizeof(vg_lite_buffer_t));
        if (!s_context.scissor_layer) {
            return VG_LITE_OUT_OF_RESOURCES;
        }

        memset(s_context.scissor_layer, 0, sizeof(vg_lite_buffer_t));
        s_context.scissor_layer->scissor_buffer = 1;
        s_context.scissor_layer->width = (target->width + 7) / 8;
        s_context.scissor_layer->height = target->height;
        s_context.scissor_layer->format = VG_LITE_A8;
        VG_LITE_RETURN_ERROR(vg_lite_allocate(s_context.scissor_layer));
    }
    s_context.scissor_layer->scissor_buffer = 1;

    /* Clear scissor layer*/
    VG_LITE_RETURN_ERROR(vg_lite_clear(s_context.scissor_layer, NULL, 0x00000000));
    vg_lite_finish();

    max_x = s_context.scissor_layer->width * 8;
    max_y = s_context.scissor_layer->height;

    /* Draw rectangle to scissor layer, one bit data of scissor layer corresponds to one pixel. */
    for (i = 0; i < nums; ++i) {
        /* Clamp the rect */
        memcpy(&rect_clamp, &rect[i], sizeof(vg_lite_rectangle_t));
        {
            if (rect_clamp.x < 0 || rect_clamp.y < 0) {
                rect_clamp.width += rect_clamp.x;
                rect_clamp.height += rect_clamp.y;
                rect_clamp.x = rect_clamp.y = 0;
            }
            if (rect_clamp.x >= max_x || rect_clamp.y >= max_y || rect_clamp.width <= 0 || rect_clamp.height <= 0) {
                rect_clamp.x = rect_clamp.y = rect_clamp.width = rect_clamp.height = 0;
            }
            if (rect_clamp.x + rect_clamp.width > max_x) {
                rect_clamp.width = max_x - rect_clamp.x;
            }
            if (rect_clamp.y + rect_clamp.height > max_y) {
                rect_clamp.height = max_y - rect_clamp.y;
            }
        }

        if (((rect_clamp.x + rect_clamp.width) >> 3) == (rect_clamp.x >> 3)) {
            rect_draw.x = rect_clamp.x / 8;
            rect_draw.y = rect_clamp.y;
            rect_draw.width = 1;
            rect_draw.height = rect_clamp.height;
            alpha = (uint8_t)(((uint8_t)(0xff >> (8 - rect_clamp.width))) << (rect_clamp.x % 8));
            stride = s_context.scissor_layer->stride;
            for (j = rect_draw.y; j < rect_draw.height + rect_draw.y; ++j) {
                ((vg_lite_uint8_t*)s_context.scissor_layer->memory)[j * stride + rect_draw.x] |= alpha;
            }
        }
        else {
            /* Split the rect */
            left_x = (rect_clamp.x % 8 == 0) ? rect_clamp.x : ((rect_clamp.x + 7) & 0xFFFFFFF8);
            right_x = (rect_clamp.x + rect_clamp.width) & 0xFFFFFFF8;
            middle_len = right_x - left_x;
            left_len = left_x - rect_clamp.x;
            right_len = rect_clamp.x + rect_clamp.width - right_x;

            /* Draw left rect */
            if (left_len) {
                rect_draw.x = rect_clamp.x / 8;
                rect_draw.y = rect_clamp.y;
                rect_draw.width = 1;
                rect_draw.height = rect_clamp.height;
                alpha = (uint8_t)(0xff << (8 - left_len));
                stride = s_context.scissor_layer->stride;
                for (j = rect_draw.y; j < rect_draw.height + rect_draw.y; ++j) {
                    ((vg_lite_uint8_t*)s_context.scissor_layer->memory)[j * stride + rect_draw.x] |= alpha;
                }
            }

            /* Draw middle rect */
            if (middle_len) {
                rect_draw.x = left_x / 8;
                rect_draw.y = rect_clamp.y;
                rect_draw.width = middle_len / 8;
                rect_draw.height = rect_clamp.height;
                VG_LITE_RETURN_ERROR(vg_lite_clear(s_context.scissor_layer, &rect_draw, 0xFFFFFFFF));
                vg_lite_finish();
            }

            /* Draw right rect */
            if (right_len) {
                rect_draw.x = (rect_clamp.x + rect_clamp.width - right_len) / 8;
                rect_draw.y = rect_clamp.y;
                rect_draw.width = 1;
                rect_draw.height = rect_clamp.height;
                alpha = (uint8_t)(0xff >> (8 - right_len));
                stride = s_context.scissor_layer->stride;
                for (j = rect_draw.y; j < rect_draw.height + rect_draw.y; ++j) {
                    ((vg_lite_uint8_t*)s_context.scissor_layer->memory)[j * stride + rect_draw.x] |= alpha;
                }
            }
        }
    }
    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A16, s_context.scissor_layer->address));
    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A17, s_context.scissor_layer->stride));
    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A1B, 0x00000100));
    vg_lite_finish();
    s_context.scissor_enable = enable;
    s_context.scissor_dirty = 1;

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_set_mirror(vg_lite_orientation_t orientation)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_set_mirror)(orientation);
#endif

#if gcFEATURE_VG_MIRROR
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_set_mirror %d\n", orientation);
#endif

    s_context.mirror_orient = orientation;
    s_context.mirror_dirty = 1;

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_set_gamma(vg_lite_gamma_conversion_t gamma_value)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_set_gamma)(gamma_value);
#endif

#if gcFEATURE_VG_GAMMA
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_set_gamma %d\n", gamma_value);
#endif

    s_context.gamma_value = gamma_value << 12;
    s_context.gamma_dirty = 1;

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

/* Set s_context.gamma_value base on target buffer */
vg_lite_void set_gamma_dest_only(vg_lite_buffer_t *target, vg_lite_int32_t stencil)
{
    uint32_t gamma_value = 0;

    /* Set gamma configuration of source buffer */
    /* Openvg paintcolor defaults to SRGB */
    s_context.gamma_src = 1;

    /* Set gamma configuration of dst buffer */
    if ((target->format >= OPENVG_lRGBX_8888 && target->format <= OPENVG_A_4) ||
        (target->format >= OPENVG_lXRGB_8888 && target->format <= OPENVG_lARGB_8888_PRE) ||
        (target->format >= OPENVG_lBGRX_8888 && target->format <= OPENVG_lBGRA_8888_PRE) ||
        (target->format >= OPENVG_lXBGR_8888 && target->format <= OPENVG_lABGR_8888_PRE) ||
        (target->format >= OPENVG_lRGBX_8888_PRE && target->format <= OPENVG_lRGBA_4444_PRE))
    {
        s_context.gamma_dst = 0;
    } 
    else
    {
        s_context.gamma_dst = 1;
    }

    if (s_context.gamma_src == 0 && s_context.gamma_dst == 1)
    {
        gamma_value = 0x00002000;
    }
    else if (s_context.gamma_src == 1 && s_context.gamma_dst == 0)
    {
        gamma_value = 0x00001000;
    }
    else
    {
        gamma_value = 0x00000000;
    }

    if (stencil && target->image_mode == VG_LITE_STENCIL_MODE)
    {
        s_context.gamma_stencil = gamma_value;
        gamma_value = 0x00000000;
    }

    if (s_context.gamma_dirty == 0 && gamma_value != s_context.gamma_value)
    {
        s_context.gamma_value = gamma_value;
        s_context.gamma_dirty = 1;
    }
}

/* Set s_context.gamma_value base on source and target buffers */
vg_lite_void get_st_gamma_src_dest(vg_lite_buffer_t *source, vg_lite_buffer_t *target)
{
    uint32_t gamma_value = 0;

    /* Set gamma configuration of source buffer */
    if ((source->format >= OPENVG_lRGBX_8888 && source->format <= OPENVG_A_4) ||
        (source->format >= OPENVG_lXRGB_8888 && source->format <= OPENVG_lARGB_8888_PRE) ||
        (source->format >= OPENVG_lBGRX_8888 && source->format <= OPENVG_lBGRA_8888_PRE) ||
        (source->format >= OPENVG_lXBGR_8888 && source->format <= OPENVG_lABGR_8888_PRE) ||
        (source->format >= OPENVG_lRGBX_8888_PRE && source->format <= OPENVG_lRGBA_4444_PRE))
    {
        s_context.gamma_src = 0;
    }
    else
    {
        s_context.gamma_src = 1;
    }
    /* Set gamma configuration of dst buffer */
    if ((target->format >= OPENVG_lRGBX_8888 && target->format <= OPENVG_A_4) ||
        (target->format >= OPENVG_lXRGB_8888 && target->format <= OPENVG_lARGB_8888_PRE) ||
        (target->format >= OPENVG_lBGRX_8888 && target->format <= OPENVG_lBGRA_8888_PRE) ||
        (target->format >= OPENVG_lXBGR_8888 && target->format <= OPENVG_lABGR_8888_PRE) ||
        (target->format >= OPENVG_lRGBX_8888_PRE && target->format <= OPENVG_lRGBA_4444_PRE))
    {
        s_context.gamma_dst = 0;
    }
    else
    {
        s_context.gamma_dst = 1;
    }

    if (s_context.gamma_src == 0 && s_context.gamma_dst == 1)
    {
        gamma_value = 0x00002000;
    }
    else if (s_context.gamma_src == 1 && s_context.gamma_dst == 0)
    {
        gamma_value = 0x00001000;
    }
    else
    {
        gamma_value = 0x00000000;
    }

    if (source->image_mode == VG_LITE_STENCIL_MODE)
    {
        if (source->paintType == VG_LITE_PAINT_PATTERN
            || source->paintType == VG_LITE_PAINT_RADIAL_GRADIENT
            || source->paintType == VG_LITE_PAINT_LINEAR_GRADIENT) {
            gamma_value = s_context.gamma_stencil;
        }
        else if (source->paintType == VG_LITE_PAINT_COLOR && s_context.gamma_dst == 0) {
            gamma_value = 0x00001000;
        }
        else {
            gamma_value = 0x00000000;
        }
    }

    if (s_context.gamma_dirty == 0 && gamma_value != s_context.gamma_value)
    {
        s_context.gamma_value = gamma_value;
        s_context.gamma_dirty = 1;
    }
}

/* Set s_context.gamma_value base on source and target buffers */
vg_lite_void save_st_gamma_src_dest(vg_lite_buffer_t *source, vg_lite_buffer_t *target)
{
    uint32_t gamma_value = 0;

    /* Set gamma configuration of source buffer */
    if ((source->format >= OPENVG_lRGBX_8888 && source->format <= OPENVG_A_4) ||
        (source->format >= OPENVG_lXRGB_8888 && source->format <= OPENVG_lARGB_8888_PRE) ||
        (source->format >= OPENVG_lBGRX_8888 && source->format <= OPENVG_lBGRA_8888_PRE) ||
        (source->format >= OPENVG_lXBGR_8888 && source->format <= OPENVG_lABGR_8888_PRE) ||
        (source->format >= OPENVG_lRGBX_8888_PRE && source->format <= OPENVG_lRGBA_4444_PRE))
    {
        s_context.gamma_src = 0;
    }
    else
    {
        s_context.gamma_src = 1;
    }
    /* Set gamma configuration of dst buffer */
    if ((target->format >= OPENVG_lRGBX_8888 && target->format <= OPENVG_A_4) ||
        (target->format >= OPENVG_lXRGB_8888 && target->format <= OPENVG_lARGB_8888_PRE) ||
        (target->format >= OPENVG_lBGRX_8888 && target->format <= OPENVG_lBGRA_8888_PRE) ||
        (target->format >= OPENVG_lXBGR_8888 && target->format <= OPENVG_lABGR_8888_PRE) ||
        (target->format >= OPENVG_lRGBX_8888_PRE && target->format <= OPENVG_lRGBA_4444_PRE))
    {
        s_context.gamma_dst = 0;
    }
    else
    {
        s_context.gamma_dst = 1;
    }

    if (s_context.gamma_src == 0 && s_context.gamma_dst == 1)
    {
        gamma_value = 0x00002000;
    }
    else if (s_context.gamma_src == 1 && s_context.gamma_dst == 0)
    {
        gamma_value = 0x00001000;
    }
    else
    {
        gamma_value = 0x00000000;
    }

    if (target->image_mode == VG_LITE_STENCIL_MODE)
    {
        s_context.gamma_stencil = gamma_value;
        gamma_value = 0x00000000;
    }

    if (s_context.gamma_dirty == 0 && gamma_value != s_context.gamma_value)
    {
        s_context.gamma_value = gamma_value;
        s_context.gamma_dirty = 1;
    }
}

vg_lite_error_t vg_lite_enable_color_transform()
{
#if DUMP_API
    FUNC_DUMP(vg_lite_enable_color_transform)();
#endif

#if gcFEATURE_VG_COLOR_TRANSFORMATION
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_enable_color_transform\n");
#endif

    s_context.color_transform = (1 << 16);

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_disable_color_transform()
{
#if DUMP_API
    FUNC_DUMP(vg_lite_disable_color_transform)();
#endif

#if gcFEATURE_VG_COLOR_TRANSFORMATION
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_disable_color_transform\n");
#endif

    s_context.color_transform = 0;

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_set_color_transform(vg_lite_color_transform_t* values)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_enable_color_transform)();
#endif

#if gcFEATURE_VG_COLOR_TRANSFORMATION
    vg_lite_error_t error = VG_LITE_SUCCESS;
    vg_lite_float_t* color_transformations = (vg_lite_float_t*)values;
    int color_elements = 0;
    short temp_transform[8] = { 0 };
    uint32_t final_transform[8] = { 0 };

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_set_color_transform %p\n", values);
#endif

    for (color_elements = 0; color_elements < 8; color_elements++) {
        if (color_elements % 2) {
            color_transformations[color_elements] = CLAMP(color_transformations[color_elements], -1.0f, 1.0f);
        }
        else {
            color_transformations[color_elements] = CLAMP(color_transformations[color_elements], -127.0f, 127.0f);
        }
        temp_transform[color_elements] = (short)(color_transformations[color_elements] * 256);
        final_transform[color_elements] = (uint32_t)temp_transform[color_elements] & 0x0000FFFF;
    }

    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A0C, final_transform[2] | final_transform[3] << 16));
    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A0D, final_transform[4] | final_transform[5] << 16));
    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A0E, final_transform[6] | final_transform[7] << 16));
    VG_LITE_RETURN_ERROR(push_state(&s_context, 0x0A0F, final_transform[0] | final_transform[1] << 16));

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

#if !gcFEATURE_VG_LVGL_SUPPORT

typedef struct {
    float                 r;
    float                 g;
    float                 b;
    float                 a;
} Color;

int colorToInt(float c, int maxc)
{
    return MIN(MAX((int)floor((double)(c * (float)maxc + 0.5f)), 0), maxc);
}

float intToColor(unsigned int i, unsigned int maxi)
{
    return (float)(i & maxi) / (float)maxi;
}

Color readPixel(vg_lite_buffer_t* src, int x, int y)
{
    unsigned int p = 0;
    Color c;
    uint8_t* scanline = (uint8_t*)src->memory + y * src->stride;

    uint8_t bitsPerPixel = 0;
    int rb = 0;
    int gb = 0;
    int bb = 0;
    int ab = 0;
    int rs = 0;
    int gs = 0;
    int bs = 0;
    int as = 0;
    switch (src->format) {
    case VG_LITE_A8:
    case VG_LITE_L8:
        ab = 8;
        bitsPerPixel = 8;
        break;
    case VG_LITE_ABGR4444:
        rs = 12;
        gs = 8;
        bs = 4;
        as = 0;
        rb = 4;
        gb = 4;
        bb = 4;
        ab = 4;
        bitsPerPixel = 16;
        break;
    case VG_LITE_ARGB4444:
        bs = 12;
        gs = 8;
        rs = 4;
        as = 0;
        rb = 4;
        gb = 4;
        bb = 4;
        ab = 4;
        bitsPerPixel = 16;
        break;
    case VG_LITE_RGBA4444:
        as = 12;
        bs = 8;
        gs = 4;
        rs = 0;
        rb = 4;
        gb = 4;
        bb = 4;
        ab = 4;
        bitsPerPixel = 16;
        break;
    case VG_LITE_BGRA4444:
        as = 12;
        rs = 8;
        gs = 4;
        bs = 0;
        rb = 4;
        gb = 4;
        bb = 4;
        ab = 4;
        bitsPerPixel = 16;
        break;
    case VG_LITE_RGB565:
        rs = 0;
        gs = 5;
        bs = 11;
        as = 0;
        rb = 5;
        gb = 6;
        bb = 5;
        ab = 0;
        bitsPerPixel = 16;
        break;
    case VG_LITE_BGR565:
        rs = 11;
        gs = 5;
        bs = 0;
        as = 0;
        rb = 5;
        gb = 6;
        bb = 5;
        ab = 0;
        bitsPerPixel = 16;
        break;
    case VG_LITE_ABGR8888:
    case VG_LITE_XBGR8888:
        rs = 24;
        gs = 16;
        bs = 8;
        as = 0;
        rb = 8;
        gb = 8;
        bb = 8;
        ab = 8;
        bitsPerPixel = 32;
        break;
    case VG_LITE_ARGB8888:
    case VG_LITE_XRGB8888:
        rs = 8;
        gs = 16;
        bs = 24;
        as = 0;
        rb = 8;
        gb = 8;
        bb = 8;
        ab = 8;
        bitsPerPixel = 32;
        break;
    case VG_LITE_RGBA8888:
    case VG_LITE_RGBX8888:
        rs = 0;
        gs = 8;
        bs = 16;
        as = 24;
        rb = 8;
        gb = 8;
        bb = 8;
        ab = 8;
        bitsPerPixel = 32;
        break;
    case VG_LITE_BGRA8888:
    case VG_LITE_BGRX8888:
        rs = 16;
        gs = 8;
        bs = 0;
        as = 24;
        rb = 8;
        gb = 8;
        bb = 8;
        ab = 8;
        bitsPerPixel = 32;
        break;
    case VG_LITE_ABGR1555:
        rs = 11;
        gs = 6;
        bs = 1;
        as = 0;
        rb = 5;
        gb = 5;
        bb = 5;
        ab = 1;
        bitsPerPixel = 16;
        break;
    case VG_LITE_RGBA5551:
        rs = 0;
        gs = 5;
        bs = 10;
        as = 15;
        rb = 5;
        gb = 5;
        bb = 5;
        ab = 1;
        bitsPerPixel = 16;
        break;
    case VG_LITE_ARGB1555:
        rs = 1;
        gs = 6;
        bs = 11;
        as = 0;
        rb = 5;
        gb = 5;
        bb = 5;
        ab = 1;
        bitsPerPixel = 16;
        break;
    case VG_LITE_BGRA5551:
        rs = 10;
        gs = 5;
        bs = 0;
        as = 15;
        rb = 5;
        gb = 5;
        bb = 5;
        ab = 1;
        bitsPerPixel = 16;
        break;
    case VG_LITE_BGRA2222:
        rs = 4;
        gs = 2;
        bs = 0;
        as = 6;
        rb = 2;
        gb = 2;
        bb = 2;
        ab = 2;
        bitsPerPixel = 8;
        break;
    case VG_LITE_RGBA2222:
        rs = 0;
        gs = 2;
        bs = 4;
        as = 6;
        rb = 2;
        gb = 2;
        bb = 2;
        ab = 2;
        bitsPerPixel = 8;
        break;
    case VG_LITE_ABGR2222:
        rs = 6;
        gs = 4;
        bs = 2;
        as = 0;
        rb = 2;
        gb = 2;
        bb = 2;
        ab = 2;
        bitsPerPixel = 8;
        break;
    case VG_LITE_ARGB2222:
        rs = 2;
        gs = 4;
        bs = 6;
        as = 0;
        rb = 2;
        gb = 2;
        bb = 2;
        ab = 2;
        bitsPerPixel = 8;
        break;
    default:
        break;
    }

    switch (bitsPerPixel)
    {
    case 32:
    {
        uint32_t* s = (((uint32_t*)scanline) + x);
        p = (unsigned int)*s;
        break;
    }

    case 16:
    {
        uint16_t* s = ((uint16_t*)scanline) + x;
        p = (unsigned int)*s;
        break;
    }

    case 8:
    {
        uint8_t* s = ((uint8_t*)scanline) + x;
        p = (unsigned int)*s;
        break;
    }
    case 4:
    {
        uint8_t* s = ((uint8_t*)scanline) + (x >> 1);
        p = (unsigned int)(*s >> ((x & 1) << 2)) & 0xf;
        break;
    }
    case 2:
    {
        uint8_t* s = ((uint8_t*)scanline) + (x >> 2);
        p = (unsigned int)(*s >> ((x & 3) << 1)) & 0x3;
        break;
    }
    default:
    {
        uint8_t* s = ((uint8_t*)scanline) + (x >> 3);
        p = (unsigned int)(*s >> (x & 7)) & 0x1;
        break;
    }
    }

    //rgba
    c.r = rb ? intToColor(p >> rs, (1 << rb) - 1) : (float)1.0f;
    c.g = gb ? intToColor(p >> gs, (1 << gb) - 1) : (float)1.0f;
    c.b = bb ? intToColor(p >> bs, (1 << bb) - 1) : (float)1.0f;
    c.a = ab ? intToColor(p >> as, (1 << ab) - 1) : (float)1.0f;

    return c;
}

void writePixel(vg_lite_buffer_t* temp, int x, int y, Color* c)
{
    uint8_t bitsPerPixel = 0;
    int rb = 0;
    int gb = 0;
    int bb = 0;
    int ab = 0;
    int rs = 0;
    int gs = 0;
    int bs = 0;
    int as = 0;
    switch (temp->format) {
    case VG_LITE_A8:
    case VG_LITE_L8:
        ab = 8;
        bitsPerPixel = 8;
        break;
    case VG_LITE_ABGR4444:
        rs = 12;
        gs = 8;
        bs = 4;
        as = 0;
        rb = 4;
        gb = 4;
        bb = 4;
        ab = 4;
        bitsPerPixel = 16;
        break;
    case VG_LITE_ARGB4444:
        bs = 12;
        gs = 8;
        rs = 4;
        as = 0;
        rb = 4;
        gb = 4;
        bb = 4;
        ab = 4;
        bitsPerPixel = 16;
        break;
    case VG_LITE_RGBA4444:
        as = 12;
        bs = 8;
        gs = 4;
        rs = 0;
        rb = 4;
        gb = 4;
        bb = 4;
        ab = 4;
        bitsPerPixel = 16;
        break;
    case VG_LITE_BGRA4444:
        as = 12;
        rs = 8;
        gs = 4;
        bs = 0;
        rb = 4;
        gb = 4;
        bb = 4;
        ab = 4;
        bitsPerPixel = 16;
        break;
    case VG_LITE_RGB565:
        rs = 0;
        gs = 5;
        bs = 11;
        as = 0;
        rb = 5;
        gb = 6;
        bb = 5;
        ab = 0;
        bitsPerPixel = 16;
        break;
    case VG_LITE_BGR565:
        rs = 11;
        gs = 5;
        bs = 0;
        as = 0;
        rb = 5;
        gb = 6;
        bb = 5;
        ab = 0;
        bitsPerPixel = 16;
        break;
    case VG_LITE_ABGR8888:
    case VG_LITE_XBGR8888:
        rs = 24;
        gs = 16;
        bs = 8;
        as = 0;
        rb = 8;
        gb = 8;
        bb = 8;
        ab = 8;
        bitsPerPixel = 32;
        break;
    case VG_LITE_ARGB8888:
    case VG_LITE_XRGB8888:
        rs = 8;
        gs = 16;
        bs = 24;
        as = 0;
        rb = 8;
        gb = 8;
        bb = 8;
        ab = 8;
        bitsPerPixel = 32;
        break;
    case VG_LITE_RGBA8888:
    case VG_LITE_RGBX8888:
        rs = 0;
        gs = 8;
        bs = 16;
        as = 24;
        rb = 8;
        gb = 8;
        bb = 8;
        ab = 8;
        bitsPerPixel = 32;
        break;
    case VG_LITE_BGRA8888:
    case VG_LITE_BGRX8888:
        rs = 16;
        gs = 8;
        bs = 0;
        as = 24;
        rb = 8;
        gb = 8;
        bb = 8;
        ab = 8;
        bitsPerPixel = 32;
        break;
    case VG_LITE_ABGR1555:
        rs = 11;
        gs = 6;
        bs = 1;
        as = 0;
        rb = 5;
        gb = 5;
        bb = 5;
        ab = 1;
        bitsPerPixel = 16;
        break;
    case VG_LITE_RGBA5551:
        rs = 0;
        gs = 5;
        bs = 10;
        as = 15;
        rb = 5;
        gb = 5;
        bb = 5;
        ab = 1;
        bitsPerPixel = 16;
        break;
    case VG_LITE_ARGB1555:
        rs = 1;
        gs = 6;
        bs = 11;
        as = 0;
        rb = 5;
        gb = 5;
        bb = 5;
        ab = 1;
        bitsPerPixel = 16;
        break;
    case VG_LITE_BGRA5551:
        rs = 10;
        gs = 5;
        bs = 0;
        as = 15;
        rb = 5;
        gb = 5;
        bb = 5;
        ab = 1;
        bitsPerPixel = 16;
        break;
    case VG_LITE_BGRA2222:
        rs = 4;
        gs = 2;
        bs = 0;
        as = 6;
        rb = 2;
        gb = 2;
        bb = 2;
        ab = 2;
        bitsPerPixel = 8;
        break;
    case VG_LITE_RGBA2222:
        rs = 0;
        gs = 2;
        bs = 4;
        as = 6;
        rb = 2;
        gb = 2;
        bb = 2;
        ab = 2;
        bitsPerPixel = 8;
        break;
    case VG_LITE_ABGR2222:
        rs = 6;
        gs = 4;
        bs = 2;
        as = 0;
        rb = 2;
        gb = 2;
        bb = 2;
        ab = 2;
        bitsPerPixel = 8;
        break;
    case VG_LITE_ARGB2222:
        rs = 2;
        gs = 4;
        bs = 6;
        as = 0;
        rb = 2;
        gb = 2;
        bb = 2;
        ab = 2;
        bitsPerPixel = 8;
        break;
    default:
        break;
    }

    unsigned int cr = rb ? colorToInt(c->r, (1 << rb) - 1) : 0;
    unsigned int cg = gb ? colorToInt(c->g, (1 << gb) - 1) : 0;
    unsigned int cb = bb ? colorToInt(c->b, (1 << bb) - 1) : 0;
    unsigned int ca = ab ? colorToInt(c->a, (1 << ab) - 1) : 0;

    unsigned int p = (cr << rs) | (cg << gs) | (cb << bs) | (ca << as);
    char* scanline = (char*)temp->memory + y * temp->stride;
    switch (bitsPerPixel)
    {
    case 32:
    {
        uint32_t* s = ((uint32_t*)scanline) + x;
        *s = (uint32_t)p;
        break;
    }

    case 16:
    {
        uint16_t* s = ((uint16_t*)scanline) + x;
        *s = (uint16_t)p;
        break;
    }

    case 8:
    {
        char* s = ((char*)scanline) + x;
        *s = (char)p;
        break;
    }
    case 4:
    {
        char* s = ((char*)scanline) + (x >> 1);
        *s = (char)((p << ((x & 1) << 2)) | ((unsigned int)*s & ~(0xf << ((x & 1) << 2))));
        break;
    }

    case 2:
    {
        char* s = ((char*)scanline) + (x >> 2);
        *s = (char)((p << ((x & 3) << 1)) | ((unsigned int)*s & ~(0x3 << ((x & 3) << 1))));
        break;
    }

    default:
    {
        break;
    }
    }
}

vg_lite_void setup_lvgl_image(vg_lite_buffer_t* dst, vg_lite_buffer_t* src, vg_lite_buffer_t* lvgl_buf, vg_lite_blend_t operation)
{
    Color c_src = {0}, c_dst = {0}, c_temp = {0};
    /* copy source region to tmp dst */
    for (int j = 0; j < src->height; j++)
    {
        for (int i = 0; i < src->width; i++)
        {
            c_src = readPixel(src, i, j);
            c_dst = readPixel(dst, i, j);

            switch (operation)
            {
            case VG_LITE_BLEND_NORMAL_LVGL:
                c_temp.a = c_src.a;
                c_temp.r = c_src.a * c_src.r;
                c_temp.g = c_src.a * c_src.g;
                c_temp.b = c_src.a * c_src.b;
                break;
            case VG_LITE_BLEND_ADDITIVE_LVGL:
                c_temp.a = c_src.a;
                c_temp.r = (c_src.r + c_dst.r) * c_src.a;
                c_temp.g = (c_src.g + c_dst.g) * c_src.a;
                c_temp.b = (c_src.b + c_dst.b) * c_src.a;
                break;
            case VG_LITE_BLEND_SUBTRACT_LVGL:
                c_temp.a = c_src.a;
                c_temp.r = c_src.r < c_dst.r ? (c_dst.r - c_src.r) * c_src.a : 0;
                c_temp.g = c_src.g < c_dst.g ? (c_dst.g - c_src.g) * c_src.a : 0;
                c_temp.b = c_src.b < c_dst.b ? (c_dst.b - c_src.b) * c_src.a : 0;
                break;
            case VG_LITE_BLEND_MULTIPLY_LVGL:
                c_temp.a = c_src.a;
                c_temp.r = c_src.r * c_dst.r * c_src.a;
                c_temp.g = c_src.g * c_dst.g * c_src.a;
                c_temp.b = c_src.b * c_dst.b * c_src.a;
                break;
            default:
                break;
            }
            if (lvgl_buf) {
                writePixel(lvgl_buf, i, j, &c_temp);
            }
#if !gcFEATURE_VG_GLOBAL_ALPHA
            c_dst.a = 1.0;
            writePixel(dst, i, j, &c_dst);
#endif
        }
    }
    return;
}

#endif  /* !gcFEATURE_VG_LVGL_SUPPORT */

/****************** FLEXA Support ***************/

vg_lite_error_t vg_lite_flexa_enable()
{
#if DUMP_API
    FUNC_DUMP(vg_lite_flexa_enable)();
#endif

#if gcFEATURE_VG_FLEXA
    vg_lite_error_t error = VG_LITE_SUCCESS;
    vg_lite_kernel_info_t data;
    uint32_t reset_bit;
    vg_lite_kernel_flexa_info_t flexa_data;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_flexa_enable\n");
#endif

    flexa_data.sbi_mode = s_context.sbi_mode = 0x1;
    flexa_data.sync_mode = s_context.sync_mode = BIT(14);
    flexa_data.flexa_mode = s_context.flexa_mode = 0x1;
    flexa_data.segment_address = s_context.segment_address;
    flexa_data.segment_count = s_context.segment_count;
    flexa_data.segment_size = s_context.segment_size;
    flexa_data.stream_id = s_context.stream_id;
    flexa_data.start_flag = s_context.start_flag = BIT(9);
    flexa_data.stop_flag = s_context.stop_flag = BIT(11);
    flexa_data.reset_flag = s_context.reset_flag = BIT(10);
    /* set sync mode */
    VG_LITE_RETURN_ERROR(vg_lite_kernel(VG_LITE_FLEXA_ENABLE, &flexa_data));

    /* check if reset is complete */
    data.addr = 0x03600;
    VG_LITE_RETURN_ERROR(vg_lite_kernel(VG_LITE_CHECK, &data));
    reset_bit = data.reg;

    if (reset_bit == 1) {
        error = VG_LITE_TIMEOUT;
        return error;
    }
    s_context.flexa_dirty = 1;

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_flexa_set_stream(vg_lite_uint8_t stream_id)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_flexa_set_stream)(stream_id);
#endif

#if gcFEATURE_VG_FLEXA
    vg_lite_error_t error = VG_LITE_SUCCESS;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_flexa_set_stream %d\n", stream_id);
#endif

    s_context.stream_id = stream_id << 1;

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_flexa_bg_buffer(vg_lite_uint8_t stream_id, vg_lite_buffer_t* buffer, vg_lite_uint32_t bg_seg_count, vg_lite_uint32_t bg_seg_size)
{
#if DUMP_API
    FUNC_DUMP(vg_lite_flexa_bg_buffer)(stream_id, buffer, bg_seg_count, bg_seg_size);
#endif

#if gcFEATURE_VG_FLEXA
    vg_lite_error_t error = VG_LITE_SUCCESS;
    vg_lite_kernel_flexa_info_t flexa_data;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_flexa_bg_buffer %d %p %d %d\n", stream_id, buffer, bg_seg_count, bg_seg_size);
#endif

    flexa_data.sbi_mode = s_context.sbi_mode;
    flexa_data.segment_address = s_context.segment_address = buffer->address;
    flexa_data.segment_count = s_context.segment_count = bg_seg_count;
    flexa_data.segment_size = s_context.segment_size = bg_seg_size;
    flexa_data.stream_id = s_context.stream_id;
    flexa_data.start_flag = s_context.start_flag;
    flexa_data.stop_flag = s_context.stop_flag;
    flexa_data.reset_flag = s_context.reset_flag;
    VG_LITE_RETURN_ERROR(vg_lite_kernel(VG_LITE_FLEXA_SET_BACKGROUND_ADDRESS, &flexa_data));

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_flexa_stop_frame()
{
#if DUMP_API
    FUNC_DUMP(vg_lite_flexa_stop_frame)();
#endif

#if gcFEATURE_VG_FLEXA
    vg_lite_error_t error = VG_LITE_SUCCESS;
    vg_lite_kernel_flexa_info_t flexa_data;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_flexa_stop_frame\n");
#endif

    flexa_data.stop_flag = s_context.stop_flag = BIT(11);
    flexa_data.stream_id = s_context.stream_id;
    flexa_data.sbi_mode = s_context.sbi_mode;
    VG_LITE_RETURN_ERROR(vg_lite_kernel(VG_LITE_FLEXA_STOP_FRAME, &flexa_data));

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

vg_lite_error_t vg_lite_flexa_disable()
{
#if DUMP_API
    FUNC_DUMP(vg_lite_flexa_disable)();
#endif

#if gcFEATURE_VG_FLEXA
    vg_lite_error_t error = VG_LITE_SUCCESS;
    vg_lite_kernel_flexa_info_t flexa_data;
    vg_lite_kernel_info_t data;
    uint32_t reset_bit;

#if gcFEATURE_VG_TRACE_API
    VGLITE_LOG("vg_lite_flexa_disable\n");
#endif

    flexa_data.flexa_mode = s_context.flexa_mode = 0x0;
    flexa_data.sync_mode = s_context.sync_mode = BIT(2);
    flexa_data.stream_id = s_context.stream_id = 0;
    flexa_data.sbi_mode = s_context.sbi_mode = 0x0;
    flexa_data.start_flag = s_context.start_flag = 0x0;
    flexa_data.stop_flag = s_context.stop_flag = 0x0;
    flexa_data.reset_flag = s_context.reset_flag = 0x0;
    VG_LITE_RETURN_ERROR(vg_lite_kernel(VG_LITE_FLEXA_DISABLE, &flexa_data));

    /* check if reset is complete */
    data.addr = 0x03600;
    VG_LITE_RETURN_ERROR(vg_lite_kernel(VG_LITE_CHECK, &data));
    reset_bit = data.reg;
    if (reset_bit == 1) {
        error = VG_LITE_TIMEOUT;
        return error;
    }
    s_context.flexa_dirty = 1;

    return error;
#else
    return VG_LITE_NOT_SUPPORT;
#endif
}

/****************** FAST_CLEAR implementation. ***************/
#if gcFEATURE_VG_IM_FASTCLEAR
static vg_lite_error_t _free_fc_buffer(vg_lite_fc_buffer_t* buffer)
{
    vg_lite_error_t error;
    vg_lite_kernel_free_t free;

    if (buffer == NULL)
        return VG_LITE_INVALID_ARGUMENT;

    /* Make sure we have a valid memory handle. */
    if (buffer->handle) {
        /* Free the buffer. */
        free.memory_handle = buffer->handle;
        VG_LITE_RETURN_ERROR(vg_lite_kernel(VG_LITE_FREE, &free));

        /* Mark the buffer as freed. */
        buffer->handle = NULL;
        buffer->memory = NULL;
    }

    return VG_LITE_SUCCESS;
}
static vg_lite_error_t convert_color(vg_lite_buffer_format_t format, uint32_t value, uint32_t* result, int* bpp)
{
    vg_lite_error_t error = VG_LITE_SUCCESS;
    uint32_t r, g, b, a;
    int Bpp = 0;

    r = B(value);
    g = G(value);
    b = R(value);
    a = A(value);

    do {
        switch (format) {
        case VG_LITE_RGBA8888:
            *result = ARGB(a, b, g, r);
            Bpp = 32;
            break;

        case VG_LITE_BGRA8888:
            *result = ARGB(a, r, g, b);
            Bpp = 32;
            break;

        case VG_LITE_RGBX8888:
            *result = ARGB(0xff, b, g, r);
            Bpp = 32;
            break;

        case VG_LITE_BGRX8888:
            *result = ARGB(0xff, r, g, b);
            Bpp = 32;
            break;


        case VG_LITE_RGBA4444:
            *result = ARGB4(a, b, g, r);
            Bpp = 16;
            break;

        case VG_LITE_BGRA4444:
            *result = ARGB4(a, r, g, b);
            Bpp = 16;
            break;

        case VG_LITE_RGB565:
            *result = ((b & 0xf8) << 8) |
                ((g & 0xfc) << 3) |
                ((r & 0xf8) >> 3);
            Bpp = 16;
            break;

        case VG_LITE_BGR565:
            *result = ((r & 0xf8) << 8) |
                ((g & 0xfc) << 3) |
                ((b & 0xf8) >> 3);
            Bpp = 16;
            break;

        case VG_LITE_BGRA5551:
            *result = ((b & 0xf8) << 8) |
                ((g & 0xf8) << 3) |
                ((r & 0xf8) >> 2) |
                ((a & 0x80) >> 7);
            Bpp = 16;
            break;

        case VG_LITE_A8:
            *result = ARGB(a, a, a, a);
            Bpp = 8;
            break;

        case VG_LITE_L8:
            *result = ARGB(r, r, r, r);
            Bpp = 8;
            break;

        default:
            error = VG_LITE_NOT_SUPPORT;
            break;
        }
    } while (0);

    if (bpp != NULL) {
        *bpp = Bpp;
    }

    if (Bpp == 16) {
        *result = ((*result) << 16) | (*result);
    }
    return error;
}

/* Fill Target buffer by FC buffer. Only used in cmodel/fpga for verification. */
#if defined(DEBUG) || defined(_DEBUG)
static vg_lite_error_t fill_fc_target(vg_lite_buffer_t* target, vg_lite_buffer_t* fcb)
{
    vg_lite_error_t error = VG_LITE_SUCCESS;
    uint8_t* fc = (uint8_t*)fcb->memory;
    uint16_t* target16;
    uint32_t* target32;
    uint8_t* target8;
    uint32_t  clear32;
    int byte_done = 0;
    int i, j, k;
    int bpp;

    do {
        convert_color(target->format, target->fc_buffer[0].color, &clear32, &bpp);

        if (bpp == 32) {
            target32 = (uint32_t*)target->memory;
            for (i = 0; i < fcb->width; i++) {

                for (j = 0; j < 8; j++) {   /* Loop the bits*/

                    if (!(((*fc) >> j) & 1)) {
                        for (k = 0; k < 64 / 4; k++) {
                            target32[k] = clear32;
                            byte_done += 4;
                            if (byte_done >= target->stride * target->height) {
                                return error;
                            }
                        }
                    }

                    target32 += 64 / 4;
                }

                fc++;
            }
        }
        else if (bpp == 16) {
            target16 = (uint16_t*)target->memory;
            for (i = 0; i < fcb->width; i++) {

                for (j = 0; j < 8; j++) {   /* Loop the bits*/

                    if (!(((*fc) >> j) & 1)) {
                        for (k = 0; k < 64 / 2; k++) {
                            target16[k] = (uint16_t)clear32;
                            byte_done += 2;
                            if (byte_done >= target->stride * target->height) {
                                return error;
                            }
                        }
                    }

                    target16 += 64 / 2;
                }

                fc++;
            }
        }
        else if (bpp == 8) {
            target8 = (uint8_t*)target->memory;
            for (i = 0; i < fcb->width; i++) {

                for (j = 0; j < 8; j++) {   /* Loop the bits*/

                    if (!(((*fc) >> j) & 1)) {
                        for (k = 0; k < 64; k++) {
                            target8[k] = (uint8_t)clear32;
                            byte_done++;
                            if (byte_done >= target->stride * target->height) {
                                return error;
                            }
                        }
                    }

                    target8 += 64;
                }

                fc++;
            }
        }
    } while (0);

    return error;
}
#endif

/* Update the fast_clear buffer when render target switched. */
static vg_lite_error_t update_fc_buffer(vg_lite_buffer_t* target)
{
    int rt_bytes;
    vg_lite_error_t error = VG_LITE_SUCCESS;
    vg_lite_context_t* context = GET_CONTEXT();
    vg_lite_kernel_allocate_t allocate;

    do {
        if (target == NULL) {
            error = VG_LITE_INVALID_ARGUMENT;
            break;
        }

        rt_bytes = target->stride * target->height;
        rt_bytes = VG_LITE_ALIGN(rt_bytes, (FC_BIT_TO_BYTES * 2));
        rt_bytes = rt_bytes / FC_BIT_TO_BYTES / 2;
        /* Only allocate new buffer when the allocated is not big enough. Yes*/
        if (rt_bytes > target->fc_buffer[0].stride) {
            _free_fc_buffer(&target->fc_buffer[0]);

            target->fc_buffer[0].width = rt_bytes;         /* The actually used bytes. */
            rt_bytes = VG_LITE_ALIGN(rt_bytes, FC_BURST_BYTES);     /* The allocated aligned bytes. */
            target->fc_buffer[0].stride = rt_bytes;
            target->fc_buffer[0].height = 1;
            allocate.bytes = rt_bytes;
            allocate.contiguous = 1;

            VG_LITE_BREAK_ERROR(vg_lite_kernel(VG_LITE_ALLOCATE, &allocate));
            target->fc_buffer[0].handle = allocate.memory_handle;
            target->fc_buffer[0].memory = allocate.memory;
            target->fc_buffer[0].address = allocate.memory_gpu;
        }
        else {
            /* Just update the fc buffer size. */
            target->fc_buffer[0].width = rt_bytes;
        }
        memset(target->fc_buffer[0].memory, 0xff, target->fc_buffer[0].stride);
        VG_LITE_RETURN_ERROR(push_state(context, 0x0A9A, target->fc_buffer[0].address));   /* FC buffer address. */
    } while (0);

    return error;
}

/* Update FC registers and clear FC buffer. */
static vg_lite_error_t clear_fc(vg_lite_fc_buffer_t* buffer, uint32_t value)
{
    vg_lite_error_t error = VG_LITE_SUCCESS;
    vg_lite_context_t* context = GET_CONTEXT();
    uint32_t bytes_to_clear;

    if (buffer == NULL)
        return VG_LITE_INVALID_ARGUMENT;
    bytes_to_clear = buffer->stride / FC_BURST_BYTES;
    buffer->color = value;

    do {
        VG_LITE_BREAK_ERROR(push_state(context, 0x0A9B, value));                       /* FC clear value. */
        VG_LITE_BREAK_ERROR(push_state(context, 0x0AB0, 0x80000000 | bytes_to_clear));   /* FC clear command. */
    } while (0);

    return error;
}

#if VG_TARGET_FC_DUMP
static int fc_buf_dump(vg_lite_buffer_t* target, vg_lite_buffer_t* fcb)
{
    int error = VG_LITE_SUCCESS;
    uint8_t* fc = (uint8_t*)fcb->memory;
    uint8_t* target8;
    int byte_done = 0;
    int target_bytes;
    int i, j;

    static unsigned s_cnt;
    unsigned cnt = s_cnt;

    FILE* fpFCBuf;
    FILE* fpTargetBuf;
    FILE* fpTargetBufInfo;
    char buf[256];

    s_cnt++;

    sprintf(buf, "gc555.fc_buf.f%04d.txt", cnt);
    fpFCBuf = fopen(buf, "wt");
    if (NULL == fpFCBuf) {
        fprintf(stderr, "[Warning] Open file \'%s\' fail.\n", buf);
        return -1;
    }

    sprintf(buf, "gc555.target_buf_info.f%04d.txt", cnt);
    fpTargetBufInfo = fopen(buf, "wt");
    if (NULL == fpTargetBufInfo) {
        fprintf(stderr, "[Warning] Open file \'%s\' fail.\n", buf);
        fclose(fpFCBuf);
        return -1;
    }
    else {
        fprintf(fpTargetBufInfo, "%-12s: %d\n", "format", target->format);
        fprintf(fpTargetBufInfo, "%-12s: %d\n", "tiled", target->tiled);
        fprintf(fpTargetBufInfo, "%-12s: %d\n", "width", target->width);
        fprintf(fpTargetBufInfo, "%-12s: %d\n", "height", target->height);
        fprintf(fpTargetBufInfo, "%-12s: %d\n", "stride", target->stride);

        fclose(fpTargetBufInfo);
    }

    sprintf(buf, "gc555.target_buf.f%04d.txt", cnt);
    fpTargetBuf = fopen(buf, "wt");
    if (NULL == fpTargetBuf) {
        fprintf(stderr, "[Warning] Open file \'%s\' fail.\n", buf);
        fclose(fpFCBuf);
        return -1;
    }

    /* Dump FC buffer & Dump target buffer */
    target8 = (uint8_t*)target->memory;
    target_bytes = target->stride * target->height;

    for (i = 0; i < fcb->width; ++i)
    {
        fprintf(fpFCBuf, "%02x\n", fc[i]);
        /* 1 byte of fc related with 512 bytes of target buffer */
        for (j = 0; j < 128; ++j) {
            fprintf(fpTargetBuf, "%02x", byte_done < target_bytes ? target8[0] : 0);
            byte_done++;

            fprintf(fpTargetBuf, "%02x", byte_done < target_bytes ? target8[1] : 0);
            byte_done++;

            fprintf(fpTargetBuf, "%02x", byte_done < target_bytes ? target8[2] : 0);
            byte_done++;

            fprintf(fpTargetBuf, "%02x\n", byte_done < target_bytes ? target8[3] : 0);
            byte_done++;

            target8 += 4;
        }
    }

    fclose(fpFCBuf);
    fclose(fpTargetBuf);

    return error;
}
#endif /* VG_TARGET_FC_DUMP */
#endif /* gcFEATURE_VG_IM_FASTCLEAR */
