Skip to content

Instantly share code, notes, and snippets.

@lecho
Created August 21, 2014 17:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lecho/a903e68fe7cccac131d0 to your computer and use it in GitHub Desktop.
Save lecho/a903e68fe7cccac131d0 to your computer and use it in GitHub Desktop.
PathCompat for android
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