Created
August 21, 2014 17:40
-
-
Save lecho/a903e68fe7cccac131d0 to your computer and use it in GitHub Desktop.
PathCompat for android
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lecho.lib.hellocharts.renderer; | |
import lecho.lib.hellocharts.util.CasteljauComputator; | |
import android.graphics.Canvas; | |
import android.graphics.Paint; | |
import android.graphics.PointF; | |
/** | |
* PathCompat uses Canvas.drawLines instead Canvas.drawPath. Supports normal lines and cubic Bezier's lines. | |
* Warning!: doesn't support breaks in line so line has to be continuous and doesn't support area chart. | |
* | |
* @author Leszek Wach | |
* | |
*/ | |
public class PathCompat { | |
/** | |
* | |
*/ | |
private final LineChartRenderer PathCompat; | |
/** | |
* @param lineChartRenderer | |
*/ | |
PathCompat(LineChartRenderer lineChartRenderer) { | |
PathCompat = lineChartRenderer; | |
} | |
private static final int DEFAULT_BUFFER_SIZE = 1024; | |
/** | |
* Bufer for point coordinates to avoid calling drawLine for every line segment, instead call drawLines. | |
*/ | |
private float[] buffer = new float[DEFAULT_BUFFER_SIZE]; | |
/** | |
* Number of points in buffer, index where put next line segment coordinate. | |
*/ | |
private int bufferIndex = 0; | |
/** | |
* De Casteljau's algorithm implementation to draw cubic Bezier's curves with hardware acceleration without | |
* using Path. For filling area Path still has to be used but it will be clipped to contentRect. | |
*/ | |
private CasteljauComputator casteljauComputator = new CasteljauComputator(); | |
/** | |
* Buffer for cubic Bezier's curve points coordinate, four points(start point, end point, two control points), | |
* two coordinate each. | |
*/ | |
private float[] bezierBuffer = new float[8]; | |
/** | |
* Computed bezier line point, as private member to avoid allocation. | |
*/ | |
private PointF bezierOutPoint = new PointF(); | |
/** | |
* Step in pixels for drawing Bezier's curve | |
*/ | |
private int pixelStep = 8; | |
public void moveTo(float x, float y) { | |
if (bufferIndex != 0) { | |
// Move too only works for starting point. | |
return; | |
} | |
buffer[bufferIndex++] = x; | |
buffer[bufferIndex++] = y; | |
} | |
public void lineTo(Canvas canvas, Paint paint, float x, float y) { | |
addLineToBuffer(x, y); | |
drawLinesIfNeeded(canvas, paint); | |
} | |
private void drawLinesIfNeeded(Canvas canvas, Paint paint) { | |
if (bufferIndex == buffer.length) { | |
// Buffer full, draw lines and remember last point as the first point in buffer. | |
canvas.drawLines(buffer, 0, bufferIndex, paint); | |
final float lastX = buffer[bufferIndex - 2]; | |
final float lastY = buffer[bufferIndex - 1]; | |
bufferIndex = 0; | |
buffer[bufferIndex++] = lastX; | |
buffer[bufferIndex++] = lastY; | |
} | |
} | |
private void addLineToBuffer(float x, float y) { | |
if (bufferIndex == 0) { | |
// No moveTo, set starting point to 0,0. | |
buffer[bufferIndex++] = 0; | |
buffer[bufferIndex++] = 0; | |
} | |
if (bufferIndex == 2) { | |
// First segment. | |
buffer[bufferIndex++] = x; | |
buffer[bufferIndex++] = y; | |
} else { | |
final float lastX = buffer[bufferIndex - 2]; | |
final float lastY = buffer[bufferIndex - 1]; | |
buffer[bufferIndex++] = lastX; | |
buffer[bufferIndex++] = lastY; | |
buffer[bufferIndex++] = x; | |
buffer[bufferIndex++] = y; | |
} | |
} | |
public void cubicTo(Canvas canvas, Paint paint, float x1, float y1, float x2, float y2, float x3, float y3) { | |
if (bufferIndex == 0) { | |
// No moveTo, set starting point to 0,0. | |
bezierBuffer[0] = 0; | |
bezierBuffer[1] = 0; | |
} else { | |
bezierBuffer[0] = buffer[bufferIndex - 2]; | |
bezierBuffer[1] = buffer[bufferIndex - 1]; | |
} | |
bezierBuffer[2] = x1; | |
bezierBuffer[3] = y1; | |
bezierBuffer[4] = x2; | |
bezierBuffer[5] = y2; | |
bezierBuffer[6] = x3; | |
bezierBuffer[7] = y3; | |
// First subline. | |
addLineToBuffer(bezierBuffer[0], bezierBuffer[1]); | |
drawLinesIfNeeded(canvas, paint); | |
final float stepT = 1.0f / ((float) Math.abs((bezierBuffer[0] - x3)) / pixelStep); | |
for (float t = stepT; t < 1.0f; t += stepT) { | |
casteljauComputator.computePoint(t, bezierBuffer, bezierOutPoint); | |
addLineToBuffer(bezierOutPoint.x, bezierOutPoint.y); | |
drawLinesIfNeeded(canvas, paint); | |
} | |
// Last subline. | |
addLineToBuffer(x3, y3); | |
drawLinesIfNeeded(canvas, paint); | |
} | |
/** | |
* Resets internal state of PathCompat and prepare it to draw next line. | |
*/ | |
public void reset() { | |
bufferIndex = 0; | |
} | |
public void drawPath(Canvas canvas, Paint paint) { | |
canvas.drawLines(buffer, 0, bufferIndex, paint); | |
bufferIndex = 0; | |
} | |
public int getStep() { | |
return pixelStep; | |
} | |
public void setStep(int step) { | |
this.pixelStep = step; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment