Crash fix - Create small clip paths (#1895)

With large data sets, the Path object created was sufficiently large as to cause an OutOfMemory error. This is resolved by only pathing a limited number of points on the chart at a time, then clearing the path and resuming. Stress testing with 1500 entries.
This commit is contained in:
Tony Patino 2016-07-01 13:19:37 -07:00
parent 87758604d3
commit b5da8dcdb5
3 changed files with 64 additions and 30 deletions

View file

@ -18,7 +18,7 @@
android:layout_margin="8dp"
android:layout_toLeftOf="@+id/tvYMax"
android:layout_marginRight="5dp"
android:max="200"
android:max="150"
android:paddingBottom="12dp" />
<SeekBar
@ -30,7 +30,7 @@
android:layout_marginBottom="35dp"
android:layout_toLeftOf="@+id/tvXMax"
android:layout_marginRight="5dp"
android:max="500"
android:max="1500"
android:paddingBottom="12dp" />
<TextView

View file

@ -111,7 +111,7 @@ public class LineChartActivity1 extends DemoBase implements OnSeekBarChangeListe
Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf");
LimitLine ll1 = new LimitLine(130f, "Upper Limit");
LimitLine ll1 = new LimitLine(150f, "Upper Limit");
ll1.setLineWidth(4f);
ll1.enableDashedLine(10f, 10f, 0f);
ll1.setLabelPosition(LimitLabelPosition.RIGHT_TOP);
@ -129,7 +129,7 @@ public class LineChartActivity1 extends DemoBase implements OnSeekBarChangeListe
leftAxis.removeAllLimitLines(); // reset all limit lines to avoid overlapping lines
leftAxis.addLimitLine(ll1);
leftAxis.addLimitLine(ll2);
leftAxis.setAxisMaxValue(220f);
leftAxis.setAxisMaxValue(200f);
leftAxis.setAxisMinValue(-50f);
//leftAxis.setYOffset(20f);
leftAxis.enableGridDashedLine(10f, 10f, 0f);

View file

@ -449,6 +449,8 @@ public class LineChartRenderer extends LineRadarRenderer {
}
}
protected Path mGenerateFilledPathBuffer = new Path();
/**
* Draws a filled linear path on the canvas.
*
@ -459,60 +461,92 @@ public class LineChartRenderer extends LineRadarRenderer {
*/
protected void drawLinearFill(Canvas c, ILineDataSet dataSet, Transformer trans, XBounds bounds) {
Path filled = generateFilledPath(dataSet, bounds);
final Path filled = mGenerateFilledPathBuffer;
trans.pathValueToPixel(filled);
final int startingIndex = bounds.min;
final int endingIndex = bounds.range + bounds.min;
final int indexInterval = 128;
final Drawable drawable = dataSet.getFillDrawable();
if (drawable != null) {
int currentStartIndex = 0;
int currentEndIndex = indexInterval;
int iterations = 0;
drawFilledPath(c, filled, drawable);
} else {
// Doing this iteratively in order to avoid OutOfMemory errors that can happen on large bounds sets.
do{
currentStartIndex = startingIndex + (iterations * indexInterval);
currentEndIndex = currentStartIndex + indexInterval;
currentEndIndex = currentEndIndex > endingIndex ? endingIndex : currentEndIndex;
if(currentStartIndex <= currentEndIndex) {
generateFilledPath(dataSet, currentStartIndex, currentEndIndex, filled);
trans.pathValueToPixel(filled);
final Drawable drawable = dataSet.getFillDrawable();
if (drawable != null) {
drawFilledPath(c, filled, drawable);
} else {
drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha());
}
}
iterations++;
}while(currentStartIndex <= currentEndIndex);
drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha());
}
}
protected Path mGenerateFilledPathBuffer = new Path();
/**
* Generates the path that is used for filled drawing.
* Generates a path that is used for filled drawing.
*
* @param dataSet The dataset from which to read the entries.
* @param startIndex The index from which to start reading the dataset
* @param endIndex The index from which to stop reading the dataset
* @param outputPath The path object that will be assigned the chart data.
*
* @param dataSet
* @return
*/
private Path generateFilledPath(ILineDataSet dataSet, XBounds bounds) {
private void generateFilledPath(final ILineDataSet dataSet, final int startIndex, final int endIndex, final Path outputPath) {
float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart);
float phaseY = mAnimator.getPhaseY();
final float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart);
final float phaseY = mAnimator.getPhaseY();
final boolean isDrawSteppedEnabled = dataSet.getMode() == LineDataSet.Mode.STEPPED;
Path filled = mGenerateFilledPathBuffer;
final Path filled = outputPath;
filled.reset();
Entry entry = dataSet.getEntryForIndex(bounds.min);
final Entry entry = dataSet.getEntryForIndex(startIndex);
filled.moveTo(entry.getX(), fillMin);
filled.lineTo(entry.getX(), entry.getY() * phaseY);
// create a new path
for (int x = bounds.min + 1; x <= bounds.range + bounds.min; x++) {
Entry currentEntry = null;
Entry previousEntry = null;
for (int x = startIndex + 1 ; x <= endIndex ; x++) {
Entry e = dataSet.getEntryForIndex(x);
currentEntry = dataSet.getEntryForIndex(x);
if (isDrawSteppedEnabled) {
final Entry ePrev = dataSet.getEntryForIndex(x - 1);
if (ePrev == null) continue;
filled.lineTo(e.getX(), ePrev.getY() * phaseY);
if (isDrawSteppedEnabled && previousEntry != null) {
filled.lineTo(currentEntry.getX(), previousEntry.getY() * phaseY);
}
filled.lineTo(e.getX(), e.getY() * phaseY);
filled.lineTo(currentEntry.getX(), currentEntry.getY() * phaseY);
previousEntry = currentEntry;
}
// close up
filled.lineTo(dataSet.getEntryForIndex(bounds.range + bounds.min).getX(), fillMin);
if(currentEntry != null) {
filled.lineTo(currentEntry.getX(), fillMin);
}
filled.close();
return filled;
}
@Override