float RCTPathAddEllipticArc(IntPtr cr,
                                    IntPtr m,
                                    CGPoint origin,
                                    CGSize size,
                                    double startAngle,
                                    double endAngle,
                                    bool clockwise
                                    )
        {
            float radius = 0;

            if (size.width != 0)
            {
                radius = size.width;
            }
            else if (size.height != 0)
            {
                radius = size.height;
            }

            Log.Info(ReactConstants.Tag, "[RCTPathAddEllipticArc] ReactBorderDrawingManager , radius:" + radius);
            Log.Info(ReactConstants.Tag, "[RCTPathAddEllipticArc] size.width:" + size.width + ", size.height:" + size.height);
            Log.Info(ReactConstants.Tag, "[RCTPathAddEllipticArc] origin.x:" + origin.x + ", origin.y:" + origin.y + ", startAngle:" + startAngle + ", endAngle:" + endAngle);

            // add a new arc path here in cairo context
            Cairo.cairo_arc(cr, origin.x, origin.y, radius, startAngle, endAngle);

            //Cairo.cairo_stroke(cairo);
            return(radius);
        }
        void RCTContextSetFillColorWithUint(IntPtr cr, uint color)
        {
            uint b = color & 0xFF;
            uint g = (color >> 8) & 0xFF;
            uint r = (color >> 16) & 0xFF;

            Cairo.cairo_set_source_rgba(cairo, r, g, b, 1);              //black alpha default
        }
        void RCTContextAddLines(IntPtr cr, CGPoint[] points, int count)
        {
            Log.Info(ReactConstants.Tag, "[RCTContextAddLines] points[0].x:" + points[0].x + ", points[0].y:" + points[0].y + ", points[1].x:" + points[1].x + ", points[1].y:" + points[1].y);
            Log.Info(ReactConstants.Tag, "[RCTContextAddLines] points[2].x:" + points[2].x + ", points[2].y:" + points[2].y + ", points[3].x:" + points[3].x + ", points[3].y:" + points[3].y);

            Cairo.cairo_move_to(cairo, points[0].x, points[0].y);
            for (int n = 1; n < count; n++)
            {
                Cairo.cairo_line_to(cairo, points[n].x, points[n].y);
            }
            //Cairo.cairo_stroke(cairo);
        }
        void RCTContextSaveCurrentImage()
        {
            // for temp test
            Cairo.cairo_surface_flush(surface);

            /* Save the generated cairo surface image data to PNG file from Memories */
            string path = Tizen.Applications.Application.Current.DirectoryInfo.Data + "CairoBorder.png";

            Cairo.cairo_surface_write_to_png(surface, path);
            pathImage = path;
            //return path;
            return;
        }
        void RCTContextUpdateCurrentImage()
        {
            // for temp test
            Cairo.cairo_surface_flush(surface);

            /* display cairo drawin on screen */
            img = Cairo.evas_object_image_filled_add(ReactProgram.RctWindow);
            IntPtr imageData = Cairo.cairo_image_surface_get_data(surface);

            Cairo.evas_object_image_data_set(img, imageData);

            Cairo.evas_object_image_data_update_add(img, 0, 0, 1920, 1080);
            return;
        }
        public void RCTUIGraphicsBeginImageContext(CGSize size, Color backgroundColor, bool hasCornerRadii, bool drawToEdge)
        {
            /// create cairo surface and context here
            surface = Cairo.cairo_image_surface_create(0, 1920, 1080);
            cairo   = Cairo.cairo_create(surface);

            /* clear background as white */
            Cairo.cairo_set_source_rgba(cairo, 1, 1, 1, 1);
            Cairo.cairo_paint(cairo);

            // set default stroke line width and color for Cairo context
            Cairo.cairo_set_line_width(cairo, 1.0);
            Cairo.cairo_set_source_rgba(cairo, 0, 0, 0, 1);              //black line
        }
        void RCTPathCreateOuterOutline(bool drawToEdge, CGRect rect, RCTCornerRadii cornerRadii)
        {
            if (drawToEdge)
            {
                Log.Info(ReactConstants.Tag, "[RCTPathCreateOuterOutline] ReactBorderDrawingManager , cairo:" + cairo);
                Log.Info(ReactConstants.Tag, "[RCTPathCreateOuterOutline] rect.origin.x:" + rect.origin.x + ", rect.origin.y:" + rect.origin.y + ", rect.size.width:" + rect.size.width + ", rect.size.height:" + rect.size.height);

                // add a new rectangle path here in cairo context
                Cairo.cairo_rectangle(cairo, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);

                pathOuter = Cairo.cairo_copy_path(cairo);
                //Cairo.cairo_stroke(cairo);

                return;
            }

            //RCTPathCreateWithRoundedRect(rect, RCTGetCornerInsets(cornerRadii, new UIEdgeInsets(0,0,0,0)));
            pathOuter = RCTPathCreateWithRoundedRect(rect, RCTGetCornerInsets(cornerRadii, new UIEdgeInsets(0, 0, 0, 0)));
        }
        void RCTGetSolidBorderImage(RCTCornerRadii cornerRadii,
                                    CGSize viewSize,
                                    UIEdgeInsets borderInsets,
                                    RCTBorderColors borderColors,
                                    //uint backgroundColor,
                                    Color backgroundColor,
                                    bool drawToEdge)
        {
            bool            hasCornerRadii = RCTCornerRadiiAreAboveThreshold(cornerRadii);
            RCTCornerInsets cornerInsets   = RCTGetCornerInsets(cornerRadii, borderInsets);

            Log.Info(ReactConstants.Tag, "[RCTGetSolidBorderImage] ReactBorderDrawingManager::RCTGetSolidBorderImage BGN ");
            Log.Info(ReactConstants.Tag, "[RCTGetSolidBorderImage] ReactBorderDrawingManager , hasCornerRadii:" + hasCornerRadii);

            bool makeStretchable =
                (borderInsets.left + cornerInsets.topLeft.width +
                 borderInsets.right + cornerInsets.bottomRight.width <= viewSize.width) &&
                (borderInsets.left + cornerInsets.bottomLeft.width +
                 borderInsets.right + cornerInsets.topRight.width <= viewSize.width) &&
                (borderInsets.top + cornerInsets.topLeft.height +
                 borderInsets.bottom + cornerInsets.bottomRight.height <= viewSize.height) &&
                (borderInsets.top + cornerInsets.topRight.height +
                 borderInsets.bottom + cornerInsets.bottomLeft.height <= viewSize.height);


            UIEdgeInsets edgeInsets = new UIEdgeInsets
                                      (
                borderInsets.top + Math.Max(cornerInsets.topLeft.height, cornerInsets.topRight.height),
                borderInsets.left + Math.Max(cornerInsets.topLeft.width, cornerInsets.bottomLeft.width),
                borderInsets.bottom + Math.Max(cornerInsets.bottomLeft.height, cornerInsets.bottomRight.height),
                borderInsets.right + Math.Max(cornerInsets.bottomRight.width, cornerInsets.topRight.width)
                                      );

            makeStretchable = false;
            Log.Info(ReactConstants.Tag, "[RCTGetSolidBorderImage] ReactBorderDrawingManager , makeStretchable:" + makeStretchable);

            CGSize sizeStretch = new CGSize
                                 (
                // 1pt for the middle stretchable area along each axis
                edgeInsets.left + 1 + edgeInsets.right,
                edgeInsets.top + 1 + edgeInsets.bottom
                                 );
            //CGSize size = makeStretchable ?  sizeStretch: viewSize;
            CGSize size = viewSize;

            // begin libCairo engine drawing context
            RCTUIGraphicsBeginImageContext(size, backgroundColor, hasCornerRadii, drawToEdge);


            CGRect rect = new CGRect((new CGPoint(0, 0)), size);

            RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii);


            Log.Info(ReactConstants.Tag, "[RCTGetSolidBorderImage] ReactBorderDrawingManager , backgroundColor.IsDefault:" + backgroundColor.IsDefault);
            if (!backgroundColor.IsDefault)
            {
                //  paint the background color here
                Log.Info(ReactConstants.Tag, "[RCTGetSolidBorderImage] ReactBorderDrawingManager::Fill the background Color! ");
                RCTContextSetFillColorWithColor(cairo, backgroundColor);
                //RCTContextSetFillColorWithUint(cairo, backgroundColor);

                Cairo.cairo_fill(cairo);
            }


            // for Cairo instead, draw rounded Rectangle path here
            Cairo.cairo_append_path(cairo, pathOuter);
            pathInner = RCTPathCreateWithRoundedRect(UIEdgeInsetsInsetRect(rect, borderInsets), cornerInsets);
            //Cairo.cairo_append_path(cairo, pathInner);

            /// Clip the outerpath with inner path  by eod rule..
            Cairo.cairo_set_fill_rule(cairo, Cairo.cairo_fill_rule_t.CAIRO_FILL_RULE_EVEN_ODD);
            //Cairo.cairo_set_fill_rule(cairo, Cairo.cairo_fill_rule_t.CAIRO_FILL_RULE_WINDING);
            Cairo.cairo_clip(cairo);

            RCTContextSetFillColorWithUint(cairo, borderColors.left);
            Cairo.cairo_paint(cairo);


            bool hasEqualColors = RCTBorderColorsAreEqual(borderColors);

            Log.Info(ReactConstants.Tag, "[RCTGetSolidBorderImage] drawToEdge:" + drawToEdge + ", hasCornerRadii:" + hasCornerRadii + ", hasEqualColors:" + hasEqualColors);
            if ((drawToEdge || !hasCornerRadii) && hasEqualColors)
            {
                Log.Info(ReactConstants.Tag, "[RCTGetSolidBorderImage] rect.origin.x:" + rect.origin.x + ", rect.origin.y:" + rect.origin.y + ", rect.size.width:" + rect.size.width + ", rect.size.height:" + rect.size.height);

                RCTContextSetFillColorWithUint(cairo, borderColors.left);
                Cairo.cairo_paint(cairo);
            }
            else
            {
                if ((hasCornerRadii))
                {
                    Log.Info(ReactConstants.Tag, "[RCTGetSolidBorderImage] ReactBorderDrawingManager-->not support different color for rounded rectangle! ");
                    RCTContextSaveCurrentImage();
                    return;
                }

                CGPoint topLeft = new CGPoint(borderInsets.left, borderInsets.top);
                if (cornerInsets.topLeft.width > 0 && cornerInsets.topLeft.height > 0)
                {
                    CGPoint[] points = new CGPoint[2];
                    CGRect    tl     = new CGRect
                                       (
                        topLeft, new CGSize(2 * cornerInsets.topLeft.width, 2 * cornerInsets.topLeft.height)
                                       );
                    RCTEllipseGetIntersectionsWithLine(tl, CGPointZero, topLeft, points);
                    if (!Single.IsNaN(points[1].x) && !Single.IsNaN(points[1].y))
                    {
                        topLeft = points[1];
                    }
                }


                CGPoint bottomLeft = new CGPoint(borderInsets.left, size.height - borderInsets.bottom);
                if (cornerInsets.bottomLeft.width > 0 && cornerInsets.bottomLeft.height > 0)
                {
                    CGPoint[] points = new CGPoint[2];
                    CGRect    bl     = new CGRect
                                       (
                        new CGPoint(bottomLeft.x, bottomLeft.y - 2 * cornerInsets.bottomLeft.height),
                        new CGSize(2 * cornerInsets.bottomLeft.width, 2 * cornerInsets.bottomLeft.height)
                                       );
                    CGPoint ble = new CGPoint(0, size.height);
                    RCTEllipseGetIntersectionsWithLine(bl, ble, bottomLeft, points);
                    if (!Single.IsNaN(points[1].x) && !Single.IsNaN(points[1].y))
                    {
                        bottomLeft = points[1];
                    }
                }


                CGPoint topRight = new CGPoint(size.width - borderInsets.right, borderInsets.top);
                if (cornerInsets.topRight.width > 0 && cornerInsets.topRight.height > 0)
                {
                    CGPoint[] points = new CGPoint[2];
                    CGRect    tr     = new CGRect
                                       (
                        new CGPoint(topRight.x - 2 * cornerInsets.topRight.width, topRight.y),
                        new CGSize(2 * cornerInsets.topRight.width, 2 * cornerInsets.topRight.height)
                                       );
                    CGPoint tre = new CGPoint(size.width, 0);
                    RCTEllipseGetIntersectionsWithLine(tr, tre, topRight, points);
                    if (!Single.IsNaN(points[0].x) && !Single.IsNaN(points[0].y))
                    {
                        topRight = points[0];
                    }
                }


                CGPoint bottomRight = new CGPoint(size.width - borderInsets.right, size.height - borderInsets.bottom);
                if (cornerInsets.bottomRight.width > 0 && cornerInsets.bottomRight.height > 0)
                {
                    CGPoint[] points = new CGPoint[2];
                    CGRect    br     = new CGRect
                                       (
                        new CGPoint(bottomRight.x - 2 * cornerInsets.bottomRight.width, bottomRight.y - 2 * cornerInsets.bottomRight.height),
                        new CGSize(2 * cornerInsets.bottomRight.width, 2 * cornerInsets.bottomRight.height)
                                       );
                    CGPoint bre = new CGPoint(size.width, size.height);
                    RCTEllipseGetIntersectionsWithLine(br, bre, bottomRight, points);
                    if (!Single.IsNaN(points[0].x) && !Single.IsNaN(points[0].y))
                    {
                        bottomRight = points[0];
                    }
                }

                Log.Info(ReactConstants.Tag, "[RCTGetSolidBorderImage] ReactBorderDrawingManager::RCTGetSolidBorderImage BGN Draw each colored Edge!");
                Log.Info(ReactConstants.Tag, "[RCTGetSolidBorderImage] borderInsets.right:" + borderInsets.right + ", borderInsets.bottom:" + borderInsets.bottom + ", borderInsets.left:" + borderInsets.left + ", borderInsets.top:" + borderInsets.top);

                uint currentColor = 0;
                //Cairo.cairo_set_fill_rule(cairo, Cairo.cairo_fill_rule_t.CAIRO_FILL_RULE_WINDING);

                // RIGHT
                if (borderInsets.right > 0)
                {
                    CGPoint[] points =
                    {
                        new CGPoint(size.width,           0),
                        topRight,
                        bottomRight,
                        new CGPoint(size.width, size.height),
                    };

                    currentColor = borderColors.right;
                    RCTContextAddLines(cairo, points, 4);
                }


                // BOTTOM
                if (borderInsets.bottom > 0)
                {
                    CGPoint[] points =
                    {
                        new CGPoint(0,          size.height),
                        bottomLeft,
                        bottomRight,
                        new CGPoint(size.width, size.height),
                    };


                    if (!(currentColor == borderColors.bottom))
                    {
                        RCTContextSetFillColorWithUint(cairo, currentColor);
                        Cairo.cairo_fill(cairo);
                        currentColor = borderColors.bottom;
                    }
                    RCTContextAddLines(cairo, points, 4);
                }


                // LEFT
                if (borderInsets.left > 0)
                {
                    CGPoint[] points =
                    {
                        CGPointZero,
                        topLeft,
                        bottomLeft,
                        new CGPoint(0,size.height),
                    };


                    if (!(currentColor == borderColors.left))
                    {
                        RCTContextSetFillColorWithUint(cairo, currentColor);
                        Cairo.cairo_fill(cairo);
                        currentColor = borderColors.left;
                    }
                    RCTContextAddLines(cairo, points, 4);
                }


                // TOP
                if (borderInsets.top > 0)
                {
                    CGPoint[] points =
                    {
                        CGPointZero,
                        topLeft,
                        topRight,
                        new CGPoint(size.width, 0),
                    };


                    if (!(currentColor == borderColors.top))
                    {
                        RCTContextSetFillColorWithUint(cairo, currentColor);
                        Cairo.cairo_fill(cairo);
                        currentColor = borderColors.top;
                    }
                    RCTContextAddLines(cairo, points, 4);
                }


                RCTContextSetFillColorWithUint(cairo, currentColor);
                Cairo.cairo_fill(cairo);
            }

            RCTContextSaveCurrentImage();

            return;
        }
 void RCTContextSetFillColorWithColor(IntPtr cr, Color color)
 {
     // set with Color parsed value
     Cairo.cairo_set_source_rgba(cairo, color.R, color.G, color.B, color.A);
 }
        IntPtr RCTPathCreateWithRoundedRect(CGRect bounds,
                                            RCTCornerInsets cornerInsets
                                            )
        {
            Log.Info(ReactConstants.Tag, "[RCTPathCreateWithRoundedRect] ReactBorderDrawingManager::RCTPathCreateWithRoundedRect BGN ");
            Log.Info(ReactConstants.Tag, "[RCTPathCreateWithRoundedRect] bounds.origin.x:" + bounds.origin.x + ", bounds.origin.y:" + bounds.origin.y + ", bounds.size.width:" + bounds.size.width + ", bounds.size.height:" + bounds.size.height);

            float minX = RectGetMinX(bounds);
            float minY = RectGetMinY(bounds);
            float maxX = RectGetMaxX(bounds);
            float maxY = RectGetMaxY(bounds);


            CGSize topLeft = new CGSize
                             (
                Math.Max(0, Math.Min(cornerInsets.topLeft.width, bounds.size.width - cornerInsets.topRight.width)),
                Math.Max(0, Math.Min(cornerInsets.topLeft.height, bounds.size.height - cornerInsets.bottomLeft.height))
                             );
            CGSize topRight = new CGSize
                              (
                Math.Max(0, Math.Min(cornerInsets.topRight.width, bounds.size.width - cornerInsets.topLeft.width)),
                Math.Max(0, Math.Min(cornerInsets.topRight.height, bounds.size.height - cornerInsets.bottomRight.height))
                              );
            CGSize bottomLeft = new CGSize
                                (
                Math.Max(0, Math.Min(cornerInsets.bottomLeft.width, bounds.size.width - cornerInsets.bottomRight.width)),
                Math.Max(0, Math.Min(cornerInsets.bottomLeft.height, bounds.size.height - cornerInsets.topLeft.height))
                                );
            CGSize bottomRight = new CGSize
                                 (
                Math.Max(0, Math.Min(cornerInsets.bottomRight.width, bounds.size.width - cornerInsets.bottomLeft.width)),
                Math.Max(0, Math.Min(cornerInsets.bottomRight.height, bounds.size.height - cornerInsets.topRight.height))
                                 );


            CGPoint p1 = new CGPoint
                         (
                minX + topLeft.width, minY + topLeft.height
                         );
            CGPoint p2 = new CGPoint
                         (
                maxX - topRight.width, minY + topRight.height
                         );
            CGPoint p3 = new CGPoint
                         (
                maxX - bottomRight.width, maxY - bottomRight.height
                         );
            CGPoint p4 = new CGPoint
                         (
                minX + bottomLeft.width, maxY - bottomLeft.height
                         );


            // begin a new cairo drawing path without current point
            Cairo.cairo_new_sub_path(cairo);

            RCTPathAddEllipticArc(cairo, IntPtr.Zero, p1, topLeft, Math.PI, 3 * Math.PI / 2, false);
            RCTPathAddEllipticArc(cairo, IntPtr.Zero, p2, topRight, 3 * Math.PI / 2, 0, false);
            RCTPathAddEllipticArc(cairo, IntPtr.Zero, p3, bottomRight, 0, Math.PI / 2, false);
            RCTPathAddEllipticArc(cairo, IntPtr.Zero, p4, bottomLeft, Math.PI / 2, Math.PI, false);

            /// auto close the current by draw a line to the init point
            Cairo.cairo_close_path(cairo);

            IntPtr path = Cairo.cairo_copy_path(cairo);

            //Cairo.cairo_stroke(cairo);

            return(path);
        }