/**
 * @file lv_draw_vglite_arc.c
 *
 */

/**
 * MIT License
 *
 * Copyright 2021-2023 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, sublicense, 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 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS 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.
 *
 */

/*********************
 *      INCLUDES
 *********************/

#include "lv_draw_vglite_arc.h"

#if LV_USE_GPU_NXP_VG_LITE
#include "lv_vglite_buf.h"
#include <math.h>

/*********************
 *      DEFINES
 *********************/

#define T_FRACTION 16384.0f

#define DICHOTO_ITER 5

static const uint16_t TperDegree[90] = {
    0, 174, 348, 522, 697, 873, 1049, 1226, 1403, 1581,
    1759, 1938, 2117, 2297, 2477, 2658, 2839, 3020, 3202, 3384,
    3567, 3749, 3933, 4116, 4300, 4484, 4668, 4852, 5037, 5222,
    5407, 5592, 5777, 5962, 6148, 6334, 6519, 6705, 6891, 7077,
    7264, 7450, 7636, 7822, 8008, 8193, 8378, 8564, 8750, 8936,
    9122, 9309, 9495, 9681, 9867, 10052, 10238, 10424, 10609, 10794,
    10979, 11164, 11349, 11534, 11718, 11902, 12086, 12270, 12453, 12637,
    12819, 13002, 13184, 13366, 13547, 13728, 13909, 14089, 14269, 14448,
    14627, 14805, 14983, 15160, 15337, 15513, 15689, 15864, 16038, 16212
};

/**********************
 *      TYPEDEFS
 **********************/

/* intermediate arc params */
typedef struct _vg_arc {
    int32_t angle;   /* angle <90deg */
    int32_t quarter; /* 0-3 counter-clockwise */
    int32_t rad;     /* radius */
    int32_t p0x;       /* point P0 */
    int32_t p0y;
    int32_t p1x;      /* point P1 */
    int32_t p1y;
    int32_t p2x;      /* point P2 */
    int32_t p2y;
    int32_t p3x;      /* point P3 */
    int32_t p3y;
} vg_arc;

typedef struct _cubic_cont_pt {
    float p0;
    float p1;
    float p2;
    float p3;
} cubic_cont_pt;

/**********************
 *  STATIC PROTOTYPES
 **********************/

static void rotate_point(int32_t angle, int32_t * x, int32_t * y);
static void add_arc_path(int32_t * arc_path, int * pidx, int32_t radius,
                         int32_t start_angle, int32_t end_angle, const lv_point_t * center, bool cw);

/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

lv_res_t lv_gpu_nxp_vglite_draw_arc(const lv_point_t * center, int32_t radius, int32_t start_angle, int32_t end_angle,
                                    const lv_area_t * clip_area, const lv_draw_arc_dsc_t * dsc)
{
    vg_lite_error_t err = VG_LITE_SUCCESS;
    lv_color32_t col32 = {.full = lv_color_to32(dsc->color)}; /*Convert color to RGBA8888*/
    vg_lite_path_t path;
    vg_lite_color_t vgcol; /* vglite takes ABGR */
    bool donut = ((end_angle - start_angle) % 360 == 0) ? true : false;
    vg_lite_buffer_t * vgbuf = lv_vglite_get_dest_buf();

    /* path: max size = 16 cubic bezier (7 words each) */
    int32_t arc_path[16 * 7];
    lv_memset_00(arc_path, sizeof(arc_path));

    /*** Init path ***/
    lv_coord_t width = dsc->width;  /* inner arc radius = outer arc radius - width */
    if(width > (lv_coord_t)radius)
        width = radius;

    int pidx = 0;
    int32_t cp_x, cp_y; /* control point coords */

    /* first control point of curve */
    cp_x = radius;
    cp_y = 0;
    rotate_point(start_angle, &cp_x, &cp_y);
    arc_path[pidx++] = VLC_OP_MOVE;
    arc_path[pidx++] = center->x + cp_x;
    arc_path[pidx++] = center->y + cp_y;

    /* draw 1-5 outer quarters */
    add_arc_path(arc_path, &pidx, radius, start_angle, end_angle, center, true);

    if(donut) {
        /* close outer circle */
        cp_x = radius;
        cp_y = 0;
        rotate_point(start_angle, &cp_x, &cp_y);
        arc_path[pidx++] = VLC_OP_LINE;
        arc_path[pidx++] = center->x + cp_x;
        arc_path[pidx++] = center->y + cp_y;
        /* start inner circle */
        cp_x = radius - width;
        cp_y = 0;
        rotate_point(start_angle, &cp_x, &cp_y);
        arc_path[pidx++] = VLC_OP_MOVE;
        arc_path[pidx++] = center->x + cp_x;
        arc_path[pidx++] = center->y + cp_y;

    }
    else if(dsc->rounded != 0U) {    /* 1st rounded arc ending */
        cp_x = radius - width / 2;
        cp_y = 0;
        rotate_point(end_angle, &cp_x, &cp_y);
        lv_point_t round_center = {center->x + cp_x, center->y + cp_y};
        add_arc_path(arc_path, &pidx, width / 2, end_angle, (end_angle + 180),
                     &round_center, true);

    }
    else {   /* 1st flat ending */
        cp_x = radius - width;
        cp_y = 0;
        rotate_point(end_angle, &cp_x, &cp_y);
        arc_path[pidx++] = VLC_OP_LINE;
        arc_path[pidx++] = center->x + cp_x;
        arc_path[pidx++] = center->y + cp_y;
    }

    /* draw 1-5 inner quarters */
    add_arc_path(arc_path, &pidx, radius - width, start_angle, end_angle, center, false);

    /* last control point of curve */
    if(donut) {     /* close the loop */
        cp_x = radius - width;
        cp_y = 0;
        rotate_point(start_angle, &cp_x, &cp_y);
        arc_path[pidx++] = VLC_OP_LINE;
        arc_path[pidx++] = center->x + cp_x;
        arc_path[pidx++] = center->y + cp_y;

    }
    else if(dsc->rounded != 0U) {    /* 2nd rounded arc ending */
        cp_x = radius - width / 2;
        cp_y = 0;
        rotate_point(start_angle, &cp_x, &cp_y);
        lv_point_t round_center = {center->x + cp_x, center->y + cp_y};
        add_arc_path(arc_path, &pidx, width / 2, (start_angle + 180), (start_angle + 360),
                     &round_center, true);

    }
    else {   /* 2nd flat ending */
        cp_x = radius;
        cp_y = 0;
        rotate_point(start_angle, &cp_x, &cp_y);
        arc_path[pidx++] = VLC_OP_LINE;
        arc_path[pidx++] = center->x + cp_x;
        arc_path[pidx++] = center->y + cp_y;
    }

    arc_path[pidx++] = VLC_OP_END;

    err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_HIGH, (uint32_t)pidx * sizeof(int32_t), arc_path,
                            (vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1,
                            ((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f);
    VG_LITE_ERR_RETURN_INV(err, "Init path failed.");

    vg_lite_buffer_format_t color_format = LV_COLOR_DEPTH == 16 ? VG_LITE_BGRA8888 : VG_LITE_ABGR8888;
    if(lv_vglite_premult_and_swizzle(&vgcol, col32, dsc->opa, color_format) != LV_RES_OK)
        VG_LITE_RETURN_INV("Premultiplication and swizzle failed.");

    vg_lite_matrix_t matrix;
    vg_lite_identity(&matrix);

    lv_vglite_set_scissor(clip_area);

    /*** Draw arc ***/
    err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_NON_ZERO, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol);
    VG_LITE_ERR_RETURN_INV(err, "Draw arc failed.");

    if(lv_vglite_run() != LV_RES_OK)
        VG_LITE_RETURN_INV("Run failed.");

    lv_vglite_disable_scissor();

    err = vg_lite_clear_path(&path);
    VG_LITE_ERR_RETURN_INV(err, "Clear path failed.");

    return LV_RES_OK;
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

static void copy_arc(vg_arc * dst, vg_arc * src)
{
    dst->quarter = src->quarter;
    dst->rad = src->rad;
    dst->angle = src->angle;
    dst->p0x = src->p0x;
    dst->p1x = src->p1x;
    dst->p2x = src->p2x;
    dst->p3x = src->p3x;
    dst->p0y = src->p0y;
    dst->p1y = src->p1y;
    dst->p2y = src->p2y;
    dst->p3y = src->p3y;
}

/**
 * Rotate the point according given rotation angle rotation center is 0,0
 */
static void rotate_point(int32_t angle, int32_t * x, int32_t * y)
{
    int32_t ori_x = *x;
    int32_t ori_y = *y;
    int16_t alpha = (int16_t)angle;
    *x = ((lv_trigo_cos(alpha) * ori_x) / LV_TRIGO_SIN_MAX) - ((lv_trigo_sin(alpha) * ori_y) / LV_TRIGO_SIN_MAX);
    *y = ((lv_trigo_sin(alpha) * ori_x) / LV_TRIGO_SIN_MAX) + ((lv_trigo_cos(alpha) * ori_y) / LV_TRIGO_SIN_MAX);
}

/**
 * Set full arc control points depending on quarter.
 * Control points match the best approximation of a circle.
 * Arc Quarter position is:
 * Q2 | Q3
 * ---+---
 * Q1 | Q0
 */
static void set_full_arc(vg_arc * fullarc)
{
    /* the tangent lenght for the bezier circle approx */
    float tang = ((float)fullarc->rad) * BEZIER_OPTIM_CIRCLE;
    switch(fullarc->quarter) {
        case 0:
            /* first quarter */
            fullarc->p0x = fullarc->rad;
            fullarc->p0y = 0;
            fullarc->p1x = fullarc->rad;
            fullarc->p1y = (int32_t)tang;
            fullarc->p2x = (int32_t)tang;
            fullarc->p2y = fullarc->rad;
            fullarc->p3x = 0;
            fullarc->p3y = fullarc->rad;
            break;
        case 1:
            /* second quarter */
            fullarc->p0x = 0;
            fullarc->p0y = fullarc->rad;
            fullarc->p1x = 0 - (int32_t)tang;
            fullarc->p1y = fullarc->rad;
            fullarc->p2x = 0 - fullarc->rad;
            fullarc->p2y = (int32_t)tang;
            fullarc->p3x = 0 - fullarc->rad;
            fullarc->p3y = 0;
            break;
        case 2:
            /* third quarter */
            fullarc->p0x = 0 - fullarc->rad;
            fullarc->p0y = 0;
            fullarc->p1x = 0 - fullarc->rad;
            fullarc->p1y = 0 - (int32_t)tang;
            fullarc->p2x = 0 - (int32_t)tang;
            fullarc->p2y = 0 - fullarc->rad;
            fullarc->p3x = 0;
            fullarc->p3y = 0 - fullarc->rad;
            break;
        case 3:
            /* fourth quarter */
            fullarc->p0x = 0;
            fullarc->p0y = 0 - fullarc->rad;
            fullarc->p1x = (int32_t)tang;
            fullarc->p1y = 0 - fullarc->rad;
            fullarc->p2x = fullarc->rad;
            fullarc->p2y = 0 - (int32_t)tang;
            fullarc->p3x = fullarc->rad;
            fullarc->p3y = 0;
            break;
        default:
            LV_LOG_ERROR("Invalid arc quarter value.");
            break;
    }
}

/**
 * Linear interpolation between two points 'a' and 'b'
 * 't' parameter is the proportion ratio expressed in range [0 ; T_FRACTION ]
 */
static inline float lerp(float coord_a, float coord_b, uint16_t t)
{
    float tf = (float)t;
    return ((T_FRACTION - tf) * coord_a + tf * coord_b) / T_FRACTION;
}

/**
 * Computes a point of bezier curve given 't' param
 */
static inline float comp_bezier_point(float t, cubic_cont_pt cp)
{
    float t_sq = t * t;
    float inv_t_sq = (1.0f - t) * (1.0f - t);
    float apt = (1.0f - t) * inv_t_sq * cp.p0 + 3.0f * inv_t_sq * t * cp.p1 + 3.0f * (1.0f - t) * t_sq * cp.p2 + t * t_sq *
                cp.p3;
    return apt;
}

/**
 * Find parameter 't' in curve at point 'pt'
 * proceed by dichotomy on only 1 dimension,
 * works only if the curve is monotonic
 * bezier curve is defined by control points [p0 p1 p2 p3]
 * 'dec' tells if curve is decreasing (true) or increasing (false)
 */
static uint16_t get_bez_t_from_pos(float pt, cubic_cont_pt cp, bool dec)
{
    /* initialize dichotomy with boundary 't' values */
    float t_low = 0.0f;
    float t_mid = 0.5f;
    float t_hig = 1.0f;
    float a_pt;
    /* dichotomy loop */
    for(int i = 0; i < DICHOTO_ITER; i++) {
        a_pt = comp_bezier_point(t_mid, cp);
        /* check mid-point position on bezier curve versus targeted point */
        if((a_pt > pt) != dec) {
            t_hig = t_mid;
        }
        else {
            t_low = t_mid;
        }
        /* define new 't' param for mid-point */
        t_mid = (t_low + t_hig) / 2.0f;
    }
    /* return parameter 't' in integer range [0 ; T_FRACTION] */
    return (uint16_t)floorf(t_mid * T_FRACTION + 0.5f);
}

/**
 * Gives relative coords of the control points
 * for the sub-arc starting at angle with given angle span
 */
static void get_subarc_control_points(vg_arc * arc, int32_t span)
{
    vg_arc fullarc;
    fullarc.angle = arc->angle;
    fullarc.quarter = arc->quarter;
    fullarc.rad = arc->rad;
    set_full_arc(&fullarc);

    /* special case of full arc */
    if(arc->angle == 90) {
        copy_arc(arc, &fullarc);
        return;
    }

    /* compute 1st arc using the geometric construction of curve */
    uint16_t t2 = TperDegree[arc->angle + span];

    /* lerp for A */
    float a2x = lerp((float)fullarc.p0x, (float)fullarc.p1x, t2);
    float a2y = lerp((float)fullarc.p0y, (float)fullarc.p1y, t2);
    /* lerp for B */
    float b2x = lerp((float)fullarc.p1x, (float)fullarc.p2x, t2);
    float b2y = lerp((float)fullarc.p1y, (float)fullarc.p2y, t2);
    /* lerp for C */
    float c2x = lerp((float)fullarc.p2x, (float)fullarc.p3x, t2);
    float c2y = lerp((float)fullarc.p2y, (float)fullarc.p3y, t2);

    /* lerp for D */
    float d2x = lerp(a2x, b2x, t2);
    float d2y = lerp(a2y, b2y, t2);
    /* lerp for E */
    float e2x = lerp(b2x, c2x, t2);
    float e2y = lerp(b2y, c2y, t2);

    float pt2x = lerp(d2x, e2x, t2);
    float pt2y = lerp(d2y, e2y, t2);

    /* compute sub-arc using the geometric construction of curve */
    uint16_t t1 = TperDegree[arc->angle];

    /* lerp for A */
    float a1x = lerp((float)fullarc.p0x, (float)fullarc.p1x, t1);
    float a1y = lerp((float)fullarc.p0y, (float)fullarc.p1y, t1);
    /* lerp for B */
    float b1x = lerp((float)fullarc.p1x, (float)fullarc.p2x, t1);
    float b1y = lerp((float)fullarc.p1y, (float)fullarc.p2y, t1);
    /* lerp for C */
    float c1x = lerp((float)fullarc.p2x, (float)fullarc.p3x, t1);
    float c1y = lerp((float)fullarc.p2y, (float)fullarc.p3y, t1);

    /* lerp for D */
    float d1x = lerp(a1x, b1x, t1);
    float d1y = lerp(a1y, b1y, t1);
    /* lerp for E */
    float e1x = lerp(b1x, c1x, t1);
    float e1y = lerp(b1y, c1y, t1);

    float pt1x = lerp(d1x, e1x, t1);
    float pt1y = lerp(d1y, e1y, t1);

    /* find the 't3' parameter for point P(t1) on the sub-arc [P0 A2 D2 P(t2)] using dichotomy
     * use position of x axis only */
    uint16_t t3;
    t3 = get_bez_t_from_pos(pt1x,
    (cubic_cont_pt) {
        .p0 = ((float)fullarc.p0x), .p1 = a2x, .p2 = d2x, .p3 = pt2x
    },
    (bool)(pt2x < (float)fullarc.p0x));

    /* lerp for B */
    float b3x = lerp(a2x, d2x, t3);
    float b3y = lerp(a2y, d2y, t3);
    /* lerp for C */
    float c3x = lerp(d2x, pt2x, t3);
    float c3y = lerp(d2y, pt2y, t3);

    /* lerp for E */
    float e3x = lerp(b3x, c3x, t3);
    float e3y = lerp(b3y, c3y, t3);

    arc->p0x = (int32_t)floorf(0.5f + pt1x);
    arc->p0y = (int32_t)floorf(0.5f + pt1y);
    arc->p1x = (int32_t)floorf(0.5f + e3x);
    arc->p1y = (int32_t)floorf(0.5f + e3y);
    arc->p2x = (int32_t)floorf(0.5f + c3x);
    arc->p2y = (int32_t)floorf(0.5f + c3y);
    arc->p3x = (int32_t)floorf(0.5f + pt2x);
    arc->p3y = (int32_t)floorf(0.5f + pt2y);
}

/**
 * Gives relative coords of the control points
 */
static void get_arc_control_points(vg_arc * arc, bool start)
{
    vg_arc fullarc;
    fullarc.angle = arc->angle;
    fullarc.quarter = arc->quarter;
    fullarc.rad = arc->rad;
    set_full_arc(&fullarc);

    /* special case of full arc */
    if(arc->angle == 90) {
        copy_arc(arc, &fullarc);
        return;
    }

    /* compute sub-arc using the geometric construction of curve */
    uint16_t t = TperDegree[arc->angle];
    /* lerp for A */
    float ax = lerp((float)fullarc.p0x, (float)fullarc.p1x, t);
    float ay = lerp((float)fullarc.p0y, (float)fullarc.p1y, t);
    /* lerp for B */
    float bx = lerp((float)fullarc.p1x, (float)fullarc.p2x, t);
    float by = lerp((float)fullarc.p1y, (float)fullarc.p2y, t);
    /* lerp for C */
    float cx = lerp((float)fullarc.p2x, (float)fullarc.p3x, t);
    float cy = lerp((float)fullarc.p2y, (float)fullarc.p3y, t);

    /* lerp for D */
    float dx = lerp(ax, bx, t);
    float dy = lerp(ay, by, t);
    /* lerp for E */
    float ex = lerp(bx, cx, t);
    float ey = lerp(by, cy, t);

    /* sub-arc's control points are tangents of DeCasteljau's algorithm */
    if(start) {
        arc->p0x = (int32_t)floorf(0.5f + lerp(dx, ex, t));
        arc->p0y = (int32_t)floorf(0.5f + lerp(dy, ey, t));
        arc->p1x = (int32_t)floorf(0.5f + ex);
        arc->p1y = (int32_t)floorf(0.5f + ey);
        arc->p2x = (int32_t)floorf(0.5f + cx);
        arc->p2y = (int32_t)floorf(0.5f + cy);
        arc->p3x = fullarc.p3x;
        arc->p3y = fullarc.p3y;
    }
    else {
        arc->p0x = fullarc.p0x;
        arc->p0y = fullarc.p0y;
        arc->p1x = (int32_t)floorf(0.5f + ax);
        arc->p1y = (int32_t)floorf(0.5f + ay);
        arc->p2x = (int32_t)floorf(0.5f + dx);
        arc->p2y = (int32_t)floorf(0.5f + dy);
        arc->p3x = (int32_t)floorf(0.5f + lerp(dx, ex, t));
        arc->p3y = (int32_t)floorf(0.5f + lerp(dy, ey, t));
    }
}

/**
 * Add the arc control points into the path data for vglite,
 * taking into account the real center of the arc (translation).
 * arc_path: (in/out) the path data array for vglite
 * pidx: (in/out) index of last element added in arc_path
 * q_arc: (in) the arc data containing control points
 * center: (in) the center of the circle in draw coordinates
 * cw: (in) true if arc is clockwise
 */
static void add_split_arc_path(int32_t * arc_path, int * pidx, vg_arc * q_arc, const lv_point_t * center, bool cw)
{
    /* assumes first control point already in array arc_path[] */
    int idx = *pidx;
    if(cw) {
#if BEZIER_DBG_CONTROL_POINTS
        arc_path[idx++] = VLC_OP_LINE;
        arc_path[idx++] = q_arc->p1x + center->x;
        arc_path[idx++] = q_arc->p1y + center->y;
        arc_path[idx++] = VLC_OP_LINE;
        arc_path[idx++] = q_arc->p2x + center->x;
        arc_path[idx++] = q_arc->p2y + center->y;
        arc_path[idx++] = VLC_OP_LINE;
        arc_path[idx++] = q_arc->p3x + center->x;
        arc_path[idx++] = q_arc->p3y + center->y;
#else
        arc_path[idx++] = VLC_OP_CUBIC;
        arc_path[idx++] = q_arc->p1x + center->x;
        arc_path[idx++] = q_arc->p1y + center->y;
        arc_path[idx++] = q_arc->p2x + center->x;
        arc_path[idx++] = q_arc->p2y + center->y;
        arc_path[idx++] = q_arc->p3x + center->x;
        arc_path[idx++] = q_arc->p3y + center->y;
#endif
    }
    else {      /* reverse points order when counter-clockwise */
#if BEZIER_DBG_CONTROL_POINTS
        arc_path[idx++] = VLC_OP_LINE;
        arc_path[idx++] = q_arc->p2x + center->x;
        arc_path[idx++] = q_arc->p2y + center->y;
        arc_path[idx++] = VLC_OP_LINE;
        arc_path[idx++] = q_arc->p1x + center->x;
        arc_path[idx++] = q_arc->p1y + center->y;
        arc_path[idx++] = VLC_OP_LINE;
        arc_path[idx++] = q_arc->p0x + center->x;
        arc_path[idx++] = q_arc->p0y + center->y;
#else
        arc_path[idx++] = VLC_OP_CUBIC;
        arc_path[idx++] = q_arc->p2x + center->x;
        arc_path[idx++] = q_arc->p2y + center->y;
        arc_path[idx++] = q_arc->p1x + center->x;
        arc_path[idx++] = q_arc->p1y + center->y;
        arc_path[idx++] = q_arc->p0x + center->x;
        arc_path[idx++] = q_arc->p0y + center->y;
#endif
    }
    /* update index i n path array*/
    *pidx = idx;
}

static void add_arc_path(int32_t * arc_path, int * pidx, int32_t radius,
                         int32_t start_angle, int32_t end_angle, const lv_point_t * center, bool cw)
{
    if(end_angle < start_angle) end_angle += 360;

    /* set number of arcs to draw */
    vg_arc q_arc;
    int32_t start_arc_angle = start_angle % 90;
    int32_t end_arc_angle = end_angle % 90;
    int32_t inv_start_arc_angle = (start_arc_angle > 0) ? (90 - start_arc_angle) : 0;
    int32_t nbarc = (end_angle - start_angle - inv_start_arc_angle - end_arc_angle) / 90;
    q_arc.rad = radius;

    /* handle special case of start & end point in the same quarter */
    if(((start_angle / 90) == (end_angle / 90)) && (nbarc <= 0)) {
        q_arc.quarter = (start_angle / 90) % 4;
        q_arc.angle = start_arc_angle;
        get_subarc_control_points(&q_arc, end_arc_angle - start_arc_angle);
        add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
        return;
    }

    if(cw) {
        /* partial starting arc */
        if(start_arc_angle > 0) {
            q_arc.quarter = (start_angle / 90) % 4;
            q_arc.angle = start_arc_angle;
            /* get cubic points relative to center */
            get_arc_control_points(&q_arc, true);
            /* put cubic points in arc_path */
            add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
        }
        /* full arcs */
        for(int32_t q = 0; q < nbarc ; q++) {
            q_arc.quarter = (q + ((start_angle + 89) / 90)) % 4;
            q_arc.angle = 90;
            /* get cubic points relative to center */
            get_arc_control_points(&q_arc, true);   /* 2nd parameter 'start' ignored */
            /* put cubic points in arc_path */
            add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
        }
        /* partial ending arc */
        if(end_arc_angle > 0) {
            q_arc.quarter = (end_angle / 90) % 4;
            q_arc.angle = end_arc_angle;
            /* get cubic points relative to center */
            get_arc_control_points(&q_arc, false);
            /* put cubic points in arc_path */
            add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
        }

    }
    else {   /* counter clockwise */

        /* partial ending arc */
        if(end_arc_angle > 0) {
            q_arc.quarter = (end_angle / 90) % 4;
            q_arc.angle = end_arc_angle;
            /* get cubic points relative to center */
            get_arc_control_points(&q_arc, false);
            /* put cubic points in arc_path */
            add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
        }
        /* full arcs */
        for(int32_t q = nbarc - 1; q >= 0; q--) {
            q_arc.quarter = (q + ((start_angle + 89) / 90)) % 4;
            q_arc.angle = 90;
            /* get cubic points relative to center */
            get_arc_control_points(&q_arc, true);   /* 2nd parameter 'start' ignored */
            /* put cubic points in arc_path */
            add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
        }
        /* partial starting arc */
        if(start_arc_angle > 0) {
            q_arc.quarter = (start_angle / 90) % 4;
            q_arc.angle = start_arc_angle;
            /* get cubic points relative to center */
            get_arc_control_points(&q_arc, true);
            /* put cubic points in arc_path */
            add_split_arc_path(arc_path, pidx, &q_arc, center, cw);
        }
    }
}

#endif /*LV_USE_GPU_NXP_VG_LITE*/
