public override Rect GetBarBounds(BarEntry e)
#endif
        {
            IBarDataSet set = data.GetDataSetForEntry(e);

            if (set == null)
            {
#if __ANDROID__ && !SKIASHARP
                outputRect.Set(float.MinValue, float.MinValue, float.MinValue, float.MinValue);
                return;
#else
                return(Rect.Empty);
#endif
            }

            float y = e.Y;
            float x = e.X;

            float barWidth = data.BarWidth;

            float top    = x - barWidth / 2f;
            float bottom = x + barWidth / 2f;
            float left   = y >= 0 ? y : 0;
            float right  = y <= 0 ? y : 0;

#if __ANDROID__ && !SKIASHARP
            outputRect.Set(left, top, right, bottom);
            GetTransformer(set.AxisDependency).RectValueToPixel(outputRect);
#else
            var outputRect = new Rect(left, top, right, bottom);
            return(GetTransformer(set.AxisDependency).RectValueToPixel(outputRect));
#endif
        }
        public override void DrawData(SKCanvas c)
        {
            var barData = Chart.BarData;

            for (int i = 0; i < barData.DataSetCount; i++)
            {
                IBarDataSet set = barData[i];

                if (set.IsVisible)
                {
                    DrawDataSet(c, set, i);
                }
            }
        }
        void PrepareBuffer(IBarDataSet dataSet, int index)
        {
            if (Chart is IBarDataProvider dataProvider && (Chart.BarData is BarData barData))
            {
                var barWidthHalf         = barData.BarWidth / 2f;
                var bufferIndex          = 0;
                var containsStacks       = dataSet.IsStacked;
                var isInverted           = dataProvider.IsInverted(axis: dataSet.AxisDependency);
                var phaseY               = Animator.PhaseY;
                var size                 = (float)Math.Ceiling(dataSet.EntryCount * Animator.PhaseX);
                IDataSet <BarEntry> data = dataSet;
                for (int i = 0; i < size; i++)
                {
                    BarEntry e = data[i];
                    if (e == null)
                    {
                        continue;
                    }

                    var x     = e.X;
                    var left  = x - barWidthHalf;
                    var right = x + barWidthHalf;

                    var y    = e.Y;
                    var vals = e.YVals;
                    if (containsStacks && vals != null)
                    {
                        var posY = 0.0f;
                        var negY = -e.NegativeSum;

                        // fill the stack
                        foreach (var value in vals)
                        {
                            float yStart;
                            if (value == 0.0 && (posY == 0.0 || negY == 0.0))
                            {
                                // Take care of the situation of a 0.0 value, which overlaps a non-zero bar
                                y      = value;
                                yStart = y;
                            }
                            else if (value >= 0.0f)
                            {
                                y      = posY;
                                yStart = posY + value;
                                posY   = yStart;
                            }
                            else
                            {
                                y      = negY;
                                yStart = negY + Math.Abs(value);
                                negY  += Math.Abs(value);
                            }

                            var top = isInverted
                                ? (y <= yStart ? y : yStart)
                                : (y >= yStart ? y : yStart);
                            var bottom = isInverted
                                ? (y >= yStart ? y : yStart)
                                : (y <= yStart ? y : yStart);

                            // multiply the height of the rect with the phase
                            top    *= phaseY;
                            bottom *= phaseY;

                            BarBuffer[index][bufferIndex++] = new SKPoint(left, top);
                            BarBuffer[index][bufferIndex++] = new SKPoint(right, bottom);
                        }
                    }
                    else
                    {
                        float bottom, top;

                        if (isInverted)
                        {
                            bottom = y >= 0 ? y : 0;
                            top    = y <= 0 ? y : 0;
                        }
                        else
                        {
                            top    = y >= 0 ? y : 0;
                            bottom = y <= 0 ? y : 0;
                        }

                        // multiply the height of the rect with the phase
                        if (top > 0)
                        {
                            top *= phaseY;
                        }
                        else
                        {
                            bottom *= phaseY;
                        }
                        BarBuffer[index][bufferIndex++] = new SKPoint(left, top);
                        BarBuffer[index][bufferIndex++] = new SKPoint(right, bottom);
                    }
                }
            }
        }
        public override void DrawValues(SKCanvas c)
        {
            // if values are drawn
            if (IsDrawingValuesAllowed(Chart))
            {
                BarData data     = Chart.BarData;
                var     dataSets = data.DataSets;

                float valueOffsetPlus   = 4.5f.DpToPixel();
                var   drawValueAboveBar = Chart.IsDrawValueAboveBar;

                for (int i = 0; i < data.DataSetCount; i++)
                {
                    IBarDataSet barDataSet = dataSets[i];

                    if (!ShouldDrawValues(barDataSet))
                    {
                        continue;
                    }

                    IDataSet <BarEntry> dataSet = barDataSet;
                    // apply the text-styling defined by the DataSet
                    ApplyValueTextStyle(barDataSet);

                    var isInverted = Chart.IsInverted(barDataSet.AxisDependency);

                    // calculate the correct offset depending on the draw position of
                    // the value
                    float valueTextHeight = ValuePaint.MeasureHeight("8");
                    float posOffset       = (drawValueAboveBar ? -valueOffsetPlus : valueTextHeight + valueOffsetPlus);
                    float negOffset       = (drawValueAboveBar ? valueTextHeight + valueOffsetPlus : -valueOffsetPlus);

                    if (isInverted)
                    {
                        posOffset = -posOffset - valueTextHeight;
                        negOffset = -negOffset - valueTextHeight;
                    }

                    // get the buffer
                    var buffer = BarBuffer[i];

                    float phaseY = Animator.PhaseY;

                    var iconsOffset = barDataSet.IconsOffset;
                    iconsOffset.X = iconsOffset.X.DpToPixel();
                    iconsOffset.Y = iconsOffset.Y.DpToPixel();

                    // if only single values are drawn (sum)
                    if (!barDataSet.IsStacked)
                    {
                        for (int j = 0; j < buffer.Length * Animator.PhaseX; j += 2)
                        {
                            float x = (buffer[j].X + buffer[j + 1].X) / 2f;

                            if (!ViewPortHandler.IsInBoundsRight(x))
                            {
                                break;
                            }

                            if (!ViewPortHandler.IsInBoundsY(buffer[j].Y) ||
                                !ViewPortHandler.IsInBoundsLeft(x))
                            {
                                continue;
                            }

                            BarEntry entry = dataSet[j / 2];
                            float    val   = entry.Y;

                            if (barDataSet.IsDrawValuesEnabled)
                            {
                                DrawValue(c, barDataSet.ValueFormatter, val, entry, i, x,
                                          val >= 0 ?
                                          (buffer[j].Y + posOffset) :
                                          (buffer[j + 1].Y + negOffset),
                                          barDataSet.ValueTextColorAt(j / 2));
                            }

                            if (entry.Icon != null && barDataSet.IsDrawIconsEnabled)
                            {
                                float px = x;
                                float py = val >= 0 ?
                                           (buffer[j].Y + posOffset) :
                                           (buffer[j + 1].Y + negOffset);

                                px += iconsOffset.X;
                                py += iconsOffset.Y;

                                entry.Icon.Draw(
                                    c,
                                    (int)px,
                                    (int)py);
                            }
                        }

                        // if we have stacks
                    }
                    else
                    {
                        Transformer trans = Chart.GetTransformer(barDataSet.AxisDependency);

                        int bufferIndex = 0;
                        int index       = 0;
                        while (index < barDataSet.EntryCount * Animator.PhaseX)
                        {
                            BarEntry entry = dataSet[index];

                            var   vals = entry.YVals;
                            float x    = (buffer[bufferIndex].X + buffer[bufferIndex + 1].X) / 2f;

                            var color = barDataSet.ValueTextColorAt(index);

                            // we still draw stacked bars, but there is one
                            // non-stacked
                            // in between
                            if (vals == null)
                            {
                                if (!ViewPortHandler.IsInBoundsRight(x))
                                {
                                    break;
                                }

                                if (!ViewPortHandler.IsInBoundsY(buffer[bufferIndex].Y) ||
                                    !ViewPortHandler.IsInBoundsLeft(x))
                                {
                                    continue;
                                }

                                if (barDataSet.IsDrawValuesEnabled)
                                {
                                    DrawValue(c, barDataSet.ValueFormatter, entry.Y, entry, i, x,
                                              buffer[bufferIndex].Y +
                                              (entry.Y >= 0 ? posOffset : negOffset),
                                              color);
                                }

                                if (entry.Icon != null && barDataSet.IsDrawIconsEnabled)
                                {
                                    float px = x;
                                    float py = buffer[bufferIndex].Y +
                                               (entry.Y >= 0 ? posOffset : negOffset);

                                    px += iconsOffset.X;
                                    py += iconsOffset.Y;

                                    entry.Icon.Draw(
                                        c,
                                        (int)px,
                                        (int)py);
                                }

                                // draw stack values
                            }
                            else
                            {
                                SKPoint[] transformed = new SKPoint[vals.Count];

                                float posY = 0f;
                                float negY = -entry.NegativeSum;

                                for (int idx = 0; idx < transformed.Length; idx++)
                                {
                                    float value = vals[idx];
                                    float y;

                                    if (value == 0.0f && (posY == 0.0f || negY == 0.0f))
                                    {
                                        // Take care of the situation of a 0.0 value, which overlaps a non-zero bar
                                        y = value;
                                    }
                                    else if (value >= 0.0f)
                                    {
                                        posY += value;
                                        y     = posY;
                                    }
                                    else
                                    {
                                        y     = negY;
                                        negY -= value;
                                    }

                                    transformed[idx].Y = y * phaseY;
                                }

                                transformed = trans.PointValuesToPixel(transformed);

                                for (int k = 0; k < transformed.Length; k++)
                                {
                                    float val       = vals[k / 2];
                                    var   drawBelow =
                                        (val == 0.0f && negY == 0.0f && posY > 0.0f) ||
                                        val < 0.0f;
                                    float y = transformed[k].Y
                                              + (drawBelow ? negOffset : posOffset);

                                    if (!ViewPortHandler.IsInBoundsRight(x))
                                    {
                                        break;
                                    }

                                    if (!ViewPortHandler.IsInBoundsY(y) ||
                                        !ViewPortHandler.IsInBoundsLeft(x))
                                    {
                                        continue;
                                    }

                                    if (barDataSet.IsDrawValuesEnabled)
                                    {
                                        DrawValue(c,
                                                  barDataSet.ValueFormatter,
                                                  vals[k / 2],
                                                  entry,
                                                  i,
                                                  x,
                                                  y,
                                                  color);
                                    }

                                    if (entry.Icon != null && barDataSet.IsDrawIconsEnabled)
                                    {
                                        entry.Icon.Draw(c,
                                                        (int)(x + iconsOffset.X),
                                                        (int)(y + iconsOffset.Y));
                                    }
                                }
                            }

                            bufferIndex = vals == null ? bufferIndex + 2 : bufferIndex + 2 * vals.Count;
                            index++;
                        }
                    }
                }
            }
        }
        protected virtual void DrawDataSet(SKCanvas c, IBarDataSet barDataSet, int index)
        {
            Transformer trans = Chart.GetTransformer(barDataSet.AxisDependency);

            BarBorderPaint.Color       = barDataSet.BarBorderColor;
            BarBorderPaint.StrokeWidth = barDataSet.BarBorderWidth.DpToPixel();

            var drawBorder = barDataSet.BarBorderWidth > 0.0f;

            float phaseX = Animator.PhaseX;

            // draw the bar shadow before the values
            if (Chart.IsDrawBarShadow)
            {
                ShadowPaint.Color = barDataSet.BarShadowColor;
                IDataSet <BarEntry> dataSet = barDataSet;
                BarData             barData = Chart.BarData;

                float barWidth     = barData.BarWidth;
                float barWidthHalf = barWidth / 2.0f;
                float x;

                for (int i = 0, count = Math.Min((int)Math.Ceiling(barDataSet.EntryCount * phaseX), barDataSet.EntryCount);
                     i < count;
                     i++)
                {
                    BarEntry e = dataSet[i];

                    x = e.X;
                    BarShadowRectBuffer.Left  = x - barWidthHalf;
                    BarShadowRectBuffer.Right = x + barWidthHalf;

                    BarShadowRectBuffer = trans.RectValueToPixel(BarShadowRectBuffer);

                    if (!ViewPortHandler.IsInBoundsLeft(BarShadowRectBuffer.Right))
                    {
                        continue;
                    }

                    if (!ViewPortHandler.IsInBoundsRight(BarShadowRectBuffer.Left))
                    {
                        break;
                    }

                    BarShadowRectBuffer.Top    = ViewPortHandler.ContentTop;
                    BarShadowRectBuffer.Bottom = ViewPortHandler.ContentBottom;

                    c.DrawRect(BarShadowRectBuffer, ShadowPaint);
                }
            }

            PrepareBuffer(dataSet: barDataSet, index: index);
            SKPoint[] pts;
            BarBuffer[index] = pts = trans.PointValuesToPixel(BarBuffer[index]);
            // initialize the buffer
            bool isCustomFill  = barDataSet.Fills != null && barDataSet.Fills.Count != 0;
            bool isSingleColor = barDataSet.Colors.Count == 1;
            bool isInverted    = Chart.IsInverted(barDataSet.AxisDependency);

            if (isSingleColor)
            {
                RenderPaint.Color = barDataSet.Color;
            }

            for (int j = 0, pos = 0; j < pts.Length; j += 2, pos++)
            {
                if (!ViewPortHandler.IsInBoundsLeft(pts[j + 1].X))
                {
                    continue;
                }

                if (!ViewPortHandler.IsInBoundsRight(pts[j].X))
                {
                    break;
                }

                if (!isSingleColor)
                {
                    // Set the color for the currently drawn value. If the index
                    // is out of bounds, reuse colors.
                    RenderPaint.Color = barDataSet.ColorAt(pos);
                }

                if (isCustomFill)
                {
                    barDataSet.GetFill(pos)
                    .Draw(
                        c, RenderPaint,
                        pts[j].X,
                        pts[j].Y,
                        pts[j + 1].X,
                        pts[j + 1].Y,
                        isInverted ? FillDirection.Down : FillDirection.Up);
                }
                else
                {
                    DrawRect(c, pts[j].X, pts[j].Y, pts[j + 1].X,
                             pts[j + 1].Y, RenderPaint);
                }

                if (drawBorder)
                {
                    DrawRect(c, pts[j].X, pts[j].Y, pts[j + 1].X,
                             pts[j + 1].Y, BarBorderPaint);
                }
            }
        }