Skip to content

Commit 6eb44a2

Browse files
pekingmehunterstich
authored andcommitted
[ProgressIndicator] Optimized the allocation during drawing.
PiperOrigin-RevId: 696962725
1 parent 60c09d9 commit 6eb44a2

File tree

6 files changed

+70
-47
lines changed

6 files changed

+70
-47
lines changed

lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import static java.lang.Math.toDegrees;
2525

2626
import android.graphics.Canvas;
27-
import android.graphics.Matrix;
2827
import android.graphics.Paint;
2928
import android.graphics.Paint.Cap;
3029
import android.graphics.Paint.Style;
@@ -68,6 +67,10 @@ final class CircularDrawingDelegate extends DrawingDelegate<CircularProgressIndi
6867
@FloatRange(from = 0.0f, to = 1.0f)
6968
private float totalTrackLengthFraction;
7069

70+
// Pre-allocates objects used in draw().
71+
private final RectF arcBounds = new RectF();
72+
private final Pair<PathPoint, PathPoint> endPoints = new Pair<>(new PathPoint(), new PathPoint());
73+
7174
/** Instantiates CircularDrawingDelegate with the current spec. */
7275
CircularDrawingDelegate(@NonNull CircularProgressIndicatorSpec spec) {
7376
super(spec);
@@ -349,24 +352,24 @@ private void drawArc(
349352
// Draws the arc without rounded corners.
350353
float startDegreeWithoutCorners = startDegree + displayedCornerRadiusInDegree;
351354
float arcDegreeWithoutCorners = arcDegree - displayedCornerRadiusInDegree * 2;
352-
Pair<PathPoint, PathPoint> endPoints = new Pair<>(new PathPoint(), new PathPoint());
355+
endPoints.first.reset();
356+
endPoints.second.reset();
353357
if (!shouldDrawWavyPath) {
354358
endPoints.first.rotate(startDegreeWithoutCorners + 90);
355359
endPoints.first.moveAcross(-adjustedRadius);
356360
endPoints.second.rotate(startDegreeWithoutCorners + arcDegreeWithoutCorners + 90);
357361
endPoints.second.moveAcross(-adjustedRadius);
358-
RectF arcBound =
359-
new RectF(-adjustedRadius, -adjustedRadius, adjustedRadius, adjustedRadius);
360-
canvas.drawArc(arcBound, startDegreeWithoutCorners, arcDegreeWithoutCorners, false, paint);
362+
arcBounds.set(-adjustedRadius, -adjustedRadius, adjustedRadius, adjustedRadius);
363+
canvas.drawArc(arcBounds, startDegreeWithoutCorners, arcDegreeWithoutCorners, false, paint);
361364
} else {
362-
endPoints =
363-
getDisplayedPath(
364-
activePathMeasure,
365-
displayedActivePath,
366-
startDegreeWithoutCorners / 360,
367-
arcDegreeWithoutCorners / 360,
368-
amplitudeFraction,
369-
phaseFraction);
365+
calculateDisplayedPath(
366+
activePathMeasure,
367+
displayedActivePath,
368+
endPoints,
369+
startDegreeWithoutCorners / 360,
370+
arcDegreeWithoutCorners / 360,
371+
amplitudeFraction,
372+
phaseFraction);
370373
canvas.drawPath(displayedActivePath, paint);
371374
}
372375

@@ -451,7 +454,7 @@ void invalidateCachedPaths() {
451454
QUARTER_CIRCLE_CONTROL_HANDLE_LENGTH, -1, 1, -QUARTER_CIRCLE_CONTROL_HANDLE_LENGTH, 1, 0);
452455
}
453456
// Scales the circle to its radius.
454-
Matrix transform = new Matrix();
457+
transform.reset();
455458
transform.setScale(adjustedRadius, adjustedRadius);
456459
cachedActivePath.transform(transform);
457460
if (spec.hasWavyEffect(drawingDeterminateIndicator)) {
@@ -515,10 +518,10 @@ private void appendCubicPerHalfCycle(
515518
anchor2.posVec[1]);
516519
}
517520

518-
@NonNull
519-
private Pair<PathPoint, PathPoint> getDisplayedPath(
521+
private void calculateDisplayedPath(
520522
@NonNull PathMeasure pathMeasure,
521523
@NonNull Path displayedPath,
524+
@NonNull Pair<PathPoint, PathPoint> endPoints,
522525
float start,
523526
float span,
524527
float amplitudeFraction,
@@ -548,16 +551,17 @@ private Pair<PathPoint, PathPoint> getDisplayedPath(
548551
float endDistance = (start + span) * pathMeasure.getLength() / 2;
549552
pathMeasure.getSegment(startDistance, endDistance, displayedPath, true);
550553
// Gathers the position and tangent of the start and end.
551-
PathPoint startPoint = new PathPoint();
554+
PathPoint startPoint = endPoints.first;
555+
startPoint.reset();
552556
pathMeasure.getPosTan(startDistance, startPoint.posVec, startPoint.tanVec);
553-
PathPoint endPoint = new PathPoint();
557+
PathPoint endPoint = endPoints.second;
558+
endPoint.reset();
554559
pathMeasure.getPosTan(endDistance, endPoint.posVec, endPoint.tanVec);
555560
// Transforms the result path to match the canvas.
556-
Matrix transform = new Matrix();
561+
transform.reset();
557562
transform.setRotate(resultRotation);
558563
startPoint.rotate(resultRotation);
559564
endPoint.rotate(resultRotation);
560565
displayedPath.transform(transform);
561-
return new Pair<>(startPoint, endPoint);
562566
}
563567
}

lib/java/com/google/android/material/progressindicator/DeterminateDrawable.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import android.content.Context;
2424
import android.graphics.Canvas;
2525
import android.graphics.Paint.Style;
26-
import android.graphics.Rect;
2726
import android.widget.ProgressBar;
2827
import androidx.annotation.NonNull;
2928
import androidx.core.math.MathUtils;
@@ -322,8 +321,6 @@ private void maybeStartAmplitudeAnimator(int level) {
322321

323322
@Override
324323
public void draw(@NonNull Canvas canvas) {
325-
Rect clipBounds = new Rect();
326-
327324
if (getBounds().isEmpty() || !isVisible() || !canvas.getClipBounds(clipBounds)) {
328325
// Escape if bounds are empty, clip bounds are empty, or currently hidden.
329326
return;

lib/java/com/google/android/material/progressindicator/DrawableWithAnimatedVisibilityChange.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import android.graphics.ColorFilter;
2424
import android.graphics.Paint;
2525
import android.graphics.PixelFormat;
26+
import android.graphics.Rect;
2627
import android.graphics.drawable.Drawable;
2728
import android.util.Property;
2829
import androidx.annotation.FloatRange;
@@ -83,12 +84,16 @@ abstract class DrawableWithAnimatedVisibilityChange extends Drawable implements
8384
@IntRange(from = 0, to = 255)
8485
private int totalAlpha;
8586

87+
// Pre-allocates objects used in draw().
88+
@NonNull Rect clipBounds;
89+
8690
// ******************* Constructor *******************
8791

8892
DrawableWithAnimatedVisibilityChange(
8993
@NonNull Context context, @NonNull BaseProgressIndicatorSpec baseSpec) {
9094
this.context = context;
9195
this.baseSpec = baseSpec;
96+
this.clipBounds = new Rect();
9297
animatorDurationScaleProvider = new AnimatorDurationScaleProvider();
9398

9499
setAlpha(255);

lib/java/com/google/android/material/progressindicator/DrawingDelegate.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import androidx.annotation.IntRange;
3535
import androidx.annotation.NonNull;
3636
import androidx.annotation.Px;
37+
import java.util.Arrays;
3738

3839
/** A delegate abstract class for drawing the graphics in different drawable classes. */
3940
abstract class DrawingDelegate<S extends BaseProgressIndicatorSpec> {
@@ -48,8 +49,12 @@ abstract class DrawingDelegate<S extends BaseProgressIndicatorSpec> {
4849

4950
final PathMeasure activePathMeasure = new PathMeasure(cachedActivePath, /* forceClosed= */ false);
5051

52+
// Pre-allocates a Matrix to transform this point.
53+
final Matrix transform;
54+
5155
public DrawingDelegate(S spec) {
5256
this.spec = spec;
57+
this.transform = new Matrix();
5358
}
5459

5560
/**
@@ -186,9 +191,12 @@ protected class PathPoint {
186191
float[] posVec = new float[2];
187192
// The tangent vector of this point on a path. The length is not guaranteed.
188193
float[] tanVec = new float[2];
194+
// Pre-allocates a Matrix for transform this point.
195+
final Matrix transform;
189196

190197
public PathPoint() {
191198
tanVec[0] = 1;
199+
transform = new Matrix();
192200
}
193201

194202
public PathPoint(PathPoint other) {
@@ -198,6 +206,7 @@ public PathPoint(PathPoint other) {
198206
public PathPoint(float[] pos, float[] tan) {
199207
arraycopy(pos, 0, this.posVec, 0, 2);
200208
arraycopy(tan, 0, this.tanVec, 0, 2);
209+
transform = new Matrix();
201210
}
202211

203212
/** Moves this point by (x, y). */
@@ -239,10 +248,17 @@ void moveAcross(float distance) {
239248

240249
/** Rotates the coordinates by the given degrees around (0, 0). */
241250
public void rotate(float rotationDegrees) {
242-
Matrix transform = new Matrix();
251+
transform.reset();
243252
transform.setRotate(rotationDegrees);
244253
transform.mapPoints(posVec);
245254
transform.mapPoints(tanVec);
246255
}
256+
257+
public void reset() {
258+
Arrays.fill(posVec, 0);
259+
Arrays.fill(tanVec, 0);
260+
tanVec[0] = 1;
261+
transform.reset();
262+
}
247263
}
248264
}

lib/java/com/google/android/material/progressindicator/IndeterminateDrawable.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import android.animation.ObjectAnimator;
2424
import android.content.Context;
2525
import android.graphics.Canvas;
26-
import android.graphics.Rect;
2726
import android.graphics.drawable.Drawable;
2827
import android.os.Build.VERSION;
2928
import android.os.Build.VERSION_CODES;
@@ -183,8 +182,6 @@ public int getIntrinsicHeight() {
183182
/** Draws the graphics based on the progress indicator's properties and the animation states. */
184183
@Override
185184
public void draw(@NonNull Canvas canvas) {
186-
Rect clipBounds = new Rect();
187-
188185
if (getBounds().isEmpty() || !isVisible() || !canvas.getClipBounds(clipBounds)) {
189186
// Escape if bounds are empty, clip bounds are empty, or currently hidden.
190187
return;

lib/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import android.graphics.Canvas;
2828
import android.graphics.Color;
29-
import android.graphics.Matrix;
3029
import android.graphics.Paint;
3130
import android.graphics.Paint.Cap;
3231
import android.graphics.Paint.Style;
@@ -63,6 +62,9 @@ final class LinearDrawingDelegate extends DrawingDelegate<LinearProgressIndicato
6362
@FloatRange(from = 0.0f, to = 1.0f)
6463
private float totalTrackLengthFraction;
6564

65+
// Pre-allocates objects used in draw().
66+
Pair<PathPoint, PathPoint> endPoints = new Pair<>(new PathPoint(), new PathPoint());
67+
6668
/** Instantiates LinearDrawingDelegate with the current spec. */
6769
LinearDrawingDelegate(@NonNull LinearProgressIndicatorSpec spec) {
6870
super(spec);
@@ -255,7 +257,8 @@ private void drawLine(
255257
paint.setAntiAlias(true);
256258
paint.setStrokeWidth(displayedTrackThickness);
257259

258-
Pair<PathPoint, PathPoint> endPoints = new Pair<>(new PathPoint(), new PathPoint());
260+
endPoints.first.reset();
261+
endPoints.second.reset();
259262
endPoints.first.translate(startBlockCenterX + originX, 0);
260263
endPoints.second.translate(endBlockCenterX + originX, 0);
261264
if (startBlockCenterX >= endBlockCenterX) {
@@ -280,14 +283,14 @@ private void drawLine(
280283
paint);
281284
} else {
282285
// Draws a portion of the cached wavy path.
283-
endPoints =
284-
getDisplayedPath(
285-
activePathMeasure,
286-
displayedActivePath,
287-
startBlockCenterX / trackLength,
288-
endBlockCenterX / trackLength,
289-
amplitudeFraction,
290-
phaseFraction);
286+
calculateDisplayedPath(
287+
activePathMeasure,
288+
displayedActivePath,
289+
endPoints,
290+
startBlockCenterX / trackLength,
291+
endBlockCenterX / trackLength,
292+
amplitudeFraction,
293+
phaseFraction);
291294
canvas.drawPath(displayedActivePath, paint);
292295
}
293296
if (!useStrokeCap && displayedCornerRadius > 0) {
@@ -381,20 +384,20 @@ void invalidateCachedPaths() {
381384
}
382385
// Transforms the wavy path from y = -1/2 * cos(PI * x) + 1/2, as calculated above,
383386
// to y = cos(2 * PI * x / wavelength), as required in spec.
384-
Matrix transformMatrix = new Matrix();
385-
transformMatrix.setScale(adjustedWavelength / 2, -2);
386-
transformMatrix.postTranslate(0, 1);
387-
cachedActivePath.transform(transformMatrix);
387+
transform.reset();
388+
transform.setScale(adjustedWavelength / 2, -2);
389+
transform.postTranslate(0, 1);
390+
cachedActivePath.transform(transform);
388391
} else {
389392
cachedActivePath.lineTo(trackLength, 0);
390393
}
391394
activePathMeasure.setPath(cachedActivePath, /* forceNewPath= */ false);
392395
}
393396

394-
@NonNull
395-
private Pair<PathPoint, PathPoint> getDisplayedPath(
397+
private void calculateDisplayedPath(
396398
@NonNull PathMeasure pathMeasure,
397399
@NonNull Path displayedPath,
400+
@NonNull Pair<PathPoint, PathPoint> endPoints,
398401
float start,
399402
float end,
400403
float amplitudeFraction,
@@ -420,12 +423,14 @@ private Pair<PathPoint, PathPoint> getDisplayedPath(
420423
float endDistance = end * pathMeasure.getLength();
421424
pathMeasure.getSegment(startDistance, endDistance, displayedPath, true);
422425
// Gathers the position and tangent of the start and end.
423-
PathPoint startPoint = new PathPoint();
426+
PathPoint startPoint = endPoints.first;
427+
startPoint.reset();
424428
pathMeasure.getPosTan(startDistance, startPoint.posVec, startPoint.tanVec);
425-
PathPoint endPoint = new PathPoint();
429+
PathPoint endPoint = endPoints.second;
430+
endPoint.reset();
426431
pathMeasure.getPosTan(endDistance, endPoint.posVec, endPoint.tanVec);
427432
// Transforms the result path to match the canvas.
428-
Matrix transform = new Matrix();
433+
transform.reset();
429434
transform.setTranslate(resultTranslationX, 0);
430435
startPoint.translate(resultTranslationX, 0);
431436
endPoint.translate(resultTranslationX, 0);
@@ -436,6 +441,5 @@ private Pair<PathPoint, PathPoint> getDisplayedPath(
436441
endPoint.scale(1, scaleY);
437442
}
438443
displayedPath.transform(transform);
439-
return new Pair<>(startPoint, endPoint);
440444
}
441445
}

0 commit comments

Comments
 (0)