From a5b989478e3ede477cb8f1c4ebd91379df7db8f6 Mon Sep 17 00:00:00 2001 From: Hannes Achleitner Date: Fri, 2 Jan 2026 09:34:00 +0100 Subject: [PATCH] Replace CanvasReplace.save() --- .../appdev/charting/components/MarkerImage.kt | 10 +- .../appdev/charting/components/MarkerView.kt | 10 +- .../charting/renderer/PieChartRenderer.kt | 16 +- .../appdev/charting/renderer/YAxisRenderer.kt | 218 +++++++++--------- .../kotlin/info/appdev/charting/utils/Fill.kt | 15 +- 5 files changed, 134 insertions(+), 135 deletions(-) diff --git a/chartLib/src/main/kotlin/info/appdev/charting/components/MarkerImage.kt b/chartLib/src/main/kotlin/info/appdev/charting/components/MarkerImage.kt index e0a67a832..c6ce67026 100644 --- a/chartLib/src/main/kotlin/info/appdev/charting/components/MarkerImage.kt +++ b/chartLib/src/main/kotlin/info/appdev/charting/components/MarkerImage.kt @@ -10,6 +10,7 @@ import info.appdev.charting.highlight.Highlight import info.appdev.charting.utils.FSize import info.appdev.charting.utils.PointF import java.lang.ref.WeakReference +import androidx.core.graphics.withTranslation /** * View that can be displayed when selecting values in the chart. Extend this class to provide custom layouts for your markers. @@ -119,11 +120,10 @@ class MarkerImage(private var mContext: Context, drawableResourceId: Int) : IMar mDrawableBoundsCache.top + height.toInt() ) - val saveId = canvas.save() - // translate to the correct position and draw - canvas.translate(posX + offset.x, posY + offset.y) - drawable!!.draw(canvas) - canvas.restoreToCount(saveId) + canvas.withTranslation(posX + offset.x, posY + offset.y) { + // translate to the correct position and draw + drawable!!.draw(canvas) + } drawable!!.bounds = mDrawableBoundsCache } diff --git a/chartLib/src/main/kotlin/info/appdev/charting/components/MarkerView.kt b/chartLib/src/main/kotlin/info/appdev/charting/components/MarkerView.kt index 4e85643f8..4960c5d4c 100644 --- a/chartLib/src/main/kotlin/info/appdev/charting/components/MarkerView.kt +++ b/chartLib/src/main/kotlin/info/appdev/charting/components/MarkerView.kt @@ -9,6 +9,7 @@ import info.appdev.charting.data.Entry import info.appdev.charting.highlight.Highlight import info.appdev.charting.utils.PointF import java.lang.ref.WeakReference +import androidx.core.graphics.withTranslation /** * View that can be displayed when selecting values in the chart. Extend this class to provide custom layouts for your markers. @@ -91,10 +92,9 @@ open class MarkerView(context: Context?, layoutResource: Int) : RelativeLayout(c override fun draw(canvas: Canvas, posX: Float, posY: Float) { val offset: PointF = getOffsetForDrawingAtPoint(posX, posY) - val saveId = canvas.save() - // translate to the correct position and draw - canvas.translate(posX + offset.x, posY + offset.y) - draw(canvas) - canvas.restoreToCount(saveId) + canvas.withTranslation(posX + offset.x, posY + offset.y) { + // translate to the correct position and draw + draw(canvas) + } } } diff --git a/chartLib/src/main/kotlin/info/appdev/charting/renderer/PieChartRenderer.kt b/chartLib/src/main/kotlin/info/appdev/charting/renderer/PieChartRenderer.kt index 5b2d76de7..614878a05 100644 --- a/chartLib/src/main/kotlin/info/appdev/charting/renderer/PieChartRenderer.kt +++ b/chartLib/src/main/kotlin/info/appdev/charting/renderer/PieChartRenderer.kt @@ -687,16 +687,16 @@ open class PieChartRenderer( val layoutHeight = centerTextLayout!!.height.toFloat() - canvas.save() - val path = mDrawCenterTextPathBuffer - path.reset() - path.addOval(holeRect, Path.Direction.CW) - canvas.clipPath(path) + canvas.withSave { + val path = mDrawCenterTextPathBuffer + path.reset() + path.addOval(holeRect, Path.Direction.CW) + clipPath(path) - canvas.translate(boundingRect.left, boundingRect.top + (boundingRect.height() - layoutHeight) / 2f) - centerTextLayout!!.draw(canvas) + translate(boundingRect.left, boundingRect.top + (boundingRect.height() - layoutHeight) / 2f) + centerTextLayout!!.draw(this) - canvas.restore() + } PointF.recycleInstance(center) PointF.recycleInstance(offset) diff --git a/chartLib/src/main/kotlin/info/appdev/charting/renderer/YAxisRenderer.kt b/chartLib/src/main/kotlin/info/appdev/charting/renderer/YAxisRenderer.kt index 0795d5154..35fcb00fb 100644 --- a/chartLib/src/main/kotlin/info/appdev/charting/renderer/YAxisRenderer.kt +++ b/chartLib/src/main/kotlin/info/appdev/charting/renderer/YAxisRenderer.kt @@ -228,28 +228,28 @@ open class YAxisRenderer( * Draws the zero line. */ protected open fun drawZeroLine(canvas: Canvas) { - val clipRestoreCount = canvas.save() - zeroLineClippingRect.set(viewPortHandler.contentRect) - zeroLineClippingRect.inset(0f, -yAxis.zeroLineWidth) - canvas.clipRect(zeroLineClippingRect) + canvas.withSave { + zeroLineClippingRect.set(viewPortHandler.contentRect) + zeroLineClippingRect.inset(0f, -yAxis.zeroLineWidth) + canvas.clipRect(zeroLineClippingRect) - // draw zero line - val pos = transformer?.getPixelForValues(0f, 0f) - pos?.let { - zeroLinePaint.color = yAxis.zeroLineColor - zeroLinePaint.strokeWidth = yAxis.zeroLineWidth + // draw zero line + val pos = transformer?.getPixelForValues(0f, 0f) + pos?.let { + zeroLinePaint.color = yAxis.zeroLineColor + zeroLinePaint.strokeWidth = yAxis.zeroLineWidth - val zeroLinePath = drawZeroLinePath - zeroLinePath.reset() + val zeroLinePath = drawZeroLinePath + zeroLinePath.reset() - zeroLinePath.moveTo(viewPortHandler.contentLeft(), it.y.toFloat()) - zeroLinePath.lineTo(viewPortHandler.contentRight(), it.y.toFloat()) + zeroLinePath.moveTo(viewPortHandler.contentLeft(), it.y.toFloat()) + zeroLinePath.lineTo(viewPortHandler.contentRight(), it.y.toFloat()) - // draw a path because lines don't support dashing on lower android versions - canvas.drawPath(zeroLinePath, zeroLinePaint) - } + // draw a path because lines don't support dashing on lower android versions + canvas.drawPath(zeroLinePath, zeroLinePaint) + } - canvas.restoreToCount(clipRestoreCount) + } } protected var renderLimitRanges: Path = Path() @@ -384,105 +384,105 @@ open class YAxisRenderer( if (!limitRange.isEnabled) continue - val clipRestoreCount = canvas.save() - limitLineClippingRect.set(viewPortHandler.contentRect) - limitLineClippingRect.inset(0f, -limitRange.lineWidth) - canvas.clipRect(limitLineClippingRect) - - limitRangePaint.style = Paint.Style.STROKE - limitRangePaint.color = limitRange.lineColor - limitRangePaint.strokeWidth = limitRange.lineWidth - limitRangePaint.pathEffect = limitRange.dashPathEffect - - limitRangePaintFill.style = Paint.Style.FILL - limitRangePaintFill.color = limitRange.rangeColor - - ptsr[1] = limitRange.limit.high - ptsr2[1] = limitRange.limit.low - - transformer?.pointValuesToPixel(ptsr) - transformer?.pointValuesToPixel(ptsr2) - - limitRangePathFill.moveTo(viewPortHandler.contentLeft(), ptsr[1]) - limitRangePathFill.addRect( - viewPortHandler.contentLeft(), - ptsr[1], - viewPortHandler.contentRight(), - ptsr2[1], - Path.Direction.CW - ) - canvas.drawPath(limitRangePathFill, limitRangePaintFill) - limitRangePathFill.reset() - - if (limitRange.lineWidth > 0) { - limitRangePath.moveTo(viewPortHandler.contentLeft(), ptsr[1]) - limitRangePath.lineTo(viewPortHandler.contentRight(), ptsr[1]) - canvas.drawPath(limitRangePath, limitRangePaint) - - limitRangePath.moveTo(viewPortHandler.contentLeft(), ptsr2[1]) - limitRangePath.lineTo(viewPortHandler.contentRight(), ptsr2[1]) - canvas.drawPath(limitRangePath, limitRangePaint) - } + canvas.withSave { + limitLineClippingRect.set(viewPortHandler.contentRect) + limitLineClippingRect.inset(0f, -limitRange.lineWidth) + canvas.clipRect(limitLineClippingRect) - limitRangePath.reset() - - val label = limitRange.label - - // if drawing the limit-value label is enabled - if (label != null && label != "") { - limitRangePaint.style = limitRange.textStyle - limitRangePaint.pathEffect = null - limitRangePaint.color = limitRange.textColor - limitRangePaint.typeface = limitRange.typeface - limitRangePaint.strokeWidth = 0.5f - limitRangePaint.textSize = limitRange.textSize - - val labelLineHeight = limitRangePaint.calcTextHeight(label).toFloat() - val xOffset = 4f.convertDpToPixel() + limitRange.xOffset - val yOffset = limitRange.lineWidth + labelLineHeight + limitRange.yOffset - - val position = limitRange.labelPosition - - when (position) { - LimitLabelPosition.RIGHT_TOP -> { - limitRangePaint.textAlign = Align.RIGHT - canvas.drawText( - label, - viewPortHandler.contentRight() - xOffset, - ptsr[1] - yOffset + labelLineHeight, limitRangePaint - ) - } + limitRangePaint.style = Paint.Style.STROKE + limitRangePaint.color = limitRange.lineColor + limitRangePaint.strokeWidth = limitRange.lineWidth + limitRangePaint.pathEffect = limitRange.dashPathEffect + + limitRangePaintFill.style = Paint.Style.FILL + limitRangePaintFill.color = limitRange.rangeColor + + ptsr[1] = limitRange.limit.high + ptsr2[1] = limitRange.limit.low + + transformer?.pointValuesToPixel(ptsr) + transformer?.pointValuesToPixel(ptsr2) + + limitRangePathFill.moveTo(viewPortHandler.contentLeft(), ptsr[1]) + limitRangePathFill.addRect( + viewPortHandler.contentLeft(), + ptsr[1], + viewPortHandler.contentRight(), + ptsr2[1], + Path.Direction.CW + ) + canvas.drawPath(limitRangePathFill, limitRangePaintFill) + limitRangePathFill.reset() + + if (limitRange.lineWidth > 0) { + limitRangePath.moveTo(viewPortHandler.contentLeft(), ptsr[1]) + limitRangePath.lineTo(viewPortHandler.contentRight(), ptsr[1]) + canvas.drawPath(limitRangePath, limitRangePaint) + + limitRangePath.moveTo(viewPortHandler.contentLeft(), ptsr2[1]) + limitRangePath.lineTo(viewPortHandler.contentRight(), ptsr2[1]) + canvas.drawPath(limitRangePath, limitRangePaint) + } - LimitLabelPosition.RIGHT_BOTTOM -> { - limitRangePaint.textAlign = Align.RIGHT - canvas.drawText( - label, - viewPortHandler.contentRight() - xOffset, - ptsr[1] + yOffset, limitRangePaint - ) - } + limitRangePath.reset() - LimitLabelPosition.LEFT_TOP -> { - limitRangePaint.textAlign = Align.LEFT - canvas.drawText( - label, - viewPortHandler.contentLeft() + xOffset, - ptsr[1] - yOffset + labelLineHeight, limitRangePaint - ) - } + val label = limitRange.label + + // if drawing the limit-value label is enabled + if (label != null && label != "") { + limitRangePaint.style = limitRange.textStyle + limitRangePaint.pathEffect = null + limitRangePaint.color = limitRange.textColor + limitRangePaint.typeface = limitRange.typeface + limitRangePaint.strokeWidth = 0.5f + limitRangePaint.textSize = limitRange.textSize + + val labelLineHeight = limitRangePaint.calcTextHeight(label).toFloat() + val xOffset = 4f.convertDpToPixel() + limitRange.xOffset + val yOffset = limitRange.lineWidth + labelLineHeight + limitRange.yOffset + + val position = limitRange.labelPosition + + when (position) { + LimitLabelPosition.RIGHT_TOP -> { + limitRangePaint.textAlign = Align.RIGHT + canvas.drawText( + label, + viewPortHandler.contentRight() - xOffset, + ptsr[1] - yOffset + labelLineHeight, limitRangePaint + ) + } + + LimitLabelPosition.RIGHT_BOTTOM -> { + limitRangePaint.textAlign = Align.RIGHT + canvas.drawText( + label, + viewPortHandler.contentRight() - xOffset, + ptsr[1] + yOffset, limitRangePaint + ) + } - else -> { - limitRangePaint.textAlign = Align.LEFT - canvas.drawText( - label, - viewPortHandler.offsetLeft() + xOffset, - ptsr[1] + yOffset, limitRangePaint - ) + LimitLabelPosition.LEFT_TOP -> { + limitRangePaint.textAlign = Align.LEFT + canvas.drawText( + label, + viewPortHandler.contentLeft() + xOffset, + ptsr[1] - yOffset + labelLineHeight, limitRangePaint + ) + } + + else -> { + limitRangePaint.textAlign = Align.LEFT + canvas.drawText( + label, + viewPortHandler.offsetLeft() + xOffset, + ptsr[1] + yOffset, limitRangePaint + ) + } } } - } - canvas.restoreToCount(clipRestoreCount) + } } } } diff --git a/chartLib/src/main/kotlin/info/appdev/charting/utils/Fill.kt b/chartLib/src/main/kotlin/info/appdev/charting/utils/Fill.kt index eb8a124cc..c0074e92c 100644 --- a/chartLib/src/main/kotlin/info/appdev/charting/utils/Fill.kt +++ b/chartLib/src/main/kotlin/info/appdev/charting/utils/Fill.kt @@ -8,6 +8,7 @@ import android.graphics.RectF import android.graphics.Shader import android.graphics.drawable.Drawable import kotlin.math.floor +import androidx.core.graphics.withClip open class Fill { enum class Type { @@ -90,12 +91,11 @@ open class Fill { } if (this.isClipPathSupported) { - val save = canvas.save() + canvas.withClip(left, top, right, bottom) { - canvas.clipRect(left, top, right, bottom) - canvas.drawColor(mFinalColor!!) + canvas.drawColor(mFinalColor!!) - canvas.restoreToCount(save) + } } else { // save val previous = paint.style @@ -171,12 +171,11 @@ open class Fill { } if (clipRect != null && this.isClipPathSupported) { - val save = canvas.save() + canvas.withClip(path) { - canvas.clipPath(path) - canvas.drawColor(mFinalColor!!) + canvas.drawColor(mFinalColor!!) - canvas.restoreToCount(save) + } } else { // save val previous = paint.style