perf(android): reduce java object creation (#10129)

This commit is contained in:
Osei Fortune
2023-02-21 01:54:51 -04:00
committed by GitHub
parent ee92512746
commit 2da0064f7c
3 changed files with 185 additions and 40 deletions

View File

@ -17,6 +17,7 @@ import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import androidx.annotation.NonNull;
@ -276,6 +277,44 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
}
}
RectF backgroundBoundsF = new RectF();
Path backgroundPath = new Path();
RectF backgroundRect = new RectF();
Paint backgroundColorPaint = new Paint();
Path backgroundNoRepeatPath = new Path();
Paint backgroundImagePaint = new Paint();
Paint backgroundGradientPaint = new Paint();
Paint borderPaint = new Paint();
Path borderPath = new Path();
RectF borderOuterRect = new RectF();
RectF borderInnerRect = new RectF();
PointF lto = new PointF(); // left-top-outside
PointF lti = new PointF(); // left-top-inside
PointF rto = new PointF(); // right-top-outside
PointF rti = new PointF(); // right-top-outside
PointF rbo = new PointF(); // right-bottom-outside
PointF rbi = new PointF(); // right-bottom-inside
PointF lbo = new PointF(); // left-bottom-outside
PointF lbi = new PointF(); // left-bottom-inside
Paint topBorderPaint = new Paint();
Path topBorderPath = new Path();
Paint rightBorderPaint = new Paint();
Path rightBorderPath = new Path();
Paint bottomBorderPaint = new Paint();
Path bottomBorderPath = new Path();
Paint leftBorderPaint = new Paint();
Path leftBorderPath = new Path();
@Override
public void draw(Canvas canvas) {
Rect bounds = this.getBounds();
@ -287,7 +326,7 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
return;
}
RectF backgroundBoundsF = new RectF(bounds.left, bounds.top, bounds.right, bounds.bottom);
backgroundBoundsF.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
float topBackoffAntialias = calculateBackoffAntialias(this.borderTopColor, this.borderTopWidth);
float rightBackoffAntialias = calculateBackoffAntialias(this.borderRightColor, this.borderRightWidth);
@ -301,18 +340,18 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
Math.max(0, borderBottomLeftRadius + leftBackoffAntialias), Math.max(0, borderBottomLeftRadius + bottomBackoffAntialias)
};
Path backgroundPath = new Path();
RectF backgroundRect = new RectF(
leftBackoffAntialias,
backgroundPath.reset();
backgroundRect.set(leftBackoffAntialias,
topBackoffAntialias,
width - rightBackoffAntialias,
height - bottomBackoffAntialias
);
height - bottomBackoffAntialias);
backgroundPath.addRoundRect(backgroundRect, backgroundRadii, Path.Direction.CW);
// draw background
if (this.backgroundColor != 0) {
Paint backgroundColorPaint = new Paint();
backgroundColorPaint.reset();
backgroundColorPaint.setStyle(Paint.Style.FILL);
backgroundColorPaint.setColor(this.backgroundColor);
backgroundColorPaint.setAntiAlias(true);
@ -337,7 +376,9 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
}
transform.postTranslate(params.posX, params.posY);
Paint backgroundImagePaint = new Paint();
backgroundImagePaint.reset();
BitmapShader shader = new BitmapShader(
this.backgroundBitmap,
params.repeatX ? Shader.TileMode.REPEAT : Shader.TileMode.CLAMP,
@ -358,7 +399,7 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
} else {
boolean supportsPathOp = android.os.Build.VERSION.SDK_INT >= 19;
if (supportsPathOp) {
Path backgroundNoRepeatPath = new Path();
backgroundNoRepeatPath.reset();
backgroundNoRepeatPath.addRect(params.posX, params.posY, params.posX + imageWidth, params.posY + imageHeight, Path.Direction.CCW);
intersect(backgroundNoRepeatPath, backgroundPath);
canvas.drawPath(backgroundNoRepeatPath, backgroundImagePaint);
@ -374,7 +415,9 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
if (this.backgroundGradient != null) {
LinearGradientDefinition def = this.backgroundGradient;
Paint backgroundGradientPaint = new Paint();
backgroundGradientPaint.reset();
LinearGradient shader = new LinearGradient(
def.getStartX() * width, def.getStartY() * height,
def.getEndX() * width, def.getEndY() * height,
@ -394,7 +437,7 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
if (this.clipPath != null && !this.clipPath.isEmpty()) {
float borderWidth = this.getUniformBorderWidth();
if (borderWidth > 0) {
Paint borderPaint = new Paint();
borderPaint.reset();
borderPaint.setColor(this.getUniformBorderColor());
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(borderWidth);
@ -405,13 +448,15 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
} else if (this.hasUniformBorderColor()) {
// iOS and browsers use black when no color is specified.
if (borderLeftWidth > 0 || borderTopWidth > 0 || borderRightWidth > 0 || borderBottomWidth > 0) {
Paint borderPaint = new Paint();
borderPaint.reset();
borderPaint.setColor(this.getUniformBorderColor());
borderPaint.setStyle(Paint.Style.FILL);
borderPaint.setAntiAlias(true);
Path borderPath = new Path();
RectF borderOuterRect = new RectF(0, 0, width, height);
borderPath.reset();
borderOuterRect.set(0, 0, width, height);
float[] borderOuterRadii = {
borderTopLeftRadius, borderTopLeftRadius,
borderTopRightRadius, borderTopRightRadius,
@ -420,7 +465,7 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
};
borderPath.addRoundRect(borderOuterRect, borderOuterRadii, Path.Direction.CW);
RectF borderInnerRect = new RectF(
borderInnerRect.set(
borderLeftWidth,
borderTopWidth,
width - borderRightWidth,
@ -453,23 +498,25 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
// +---------------------+
//lbo rbo
PointF lto = new PointF(0, 0); // left-top-outside
PointF lti = new PointF(left, top); // left-top-inside
lto.set(0, 0); // left-top-outside
lti.set(left, top); // left-top-inside
PointF rto = new PointF(bounds.right, 0); // right-top-outside
PointF rti = new PointF(bounds.right - right, top); // right-top-outside
rto.set(bounds.right, 0); // right-top-outside
rti.set(bounds.right - right, top); // right-top-outside
PointF rbo = new PointF(bounds.right, bounds.bottom); // right-bottom-outside
PointF rbi = new PointF(bounds.right - right, bounds.bottom - bottom); // right-bottom-inside
rbo.set(bounds.right, bounds.bottom); // right-bottom-outside
rbi.set(bounds.right - right, bounds.bottom - bottom); // right-bottom-inside
lbo.set(0, bounds.bottom); // left-bottom-outside
lbi.set(left, bounds.bottom - bottom); // left-bottom-inside
PointF lbo = new PointF(0, bounds.bottom); // left-bottom-outside
PointF lbi = new PointF(left, bounds.bottom - bottom); // left-bottom-inside
if (this.borderTopWidth > 0) {
Paint topBorderPaint = new Paint();
topBorderPaint.reset();
topBorderPaint.setColor(this.borderTopColor);
topBorderPaint.setAntiAlias(true);
Path topBorderPath = new Path();
topBorderPath.reset();
topBorderPath.setFillType(Path.FillType.EVEN_ODD);
topBorderPath.moveTo(lto.x, lto.y);
topBorderPath.lineTo(rto.x, rto.y);
@ -480,10 +527,12 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
}
if (this.borderRightWidth > 0) {
Paint rightBorderPaint = new Paint();
rightBorderPaint.reset();
rightBorderPaint.setColor(this.borderRightColor);
rightBorderPaint.setAntiAlias(true);
Path rightBorderPath = new Path();
rightBorderPath.reset();
rightBorderPath.setFillType(Path.FillType.EVEN_ODD);
rightBorderPath.moveTo(rto.x, rto.y);
rightBorderPath.lineTo(rbo.x, rbo.y);
@ -494,10 +543,12 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
}
if (this.borderBottomWidth > 0) {
Paint bottomBorderPaint = new Paint();
bottomBorderPaint.reset();
bottomBorderPaint.setColor(this.borderBottomColor);
bottomBorderPaint.setAntiAlias(true);
Path bottomBorderPath = new Path();
bottomBorderPath.reset();
bottomBorderPath.setFillType(Path.FillType.EVEN_ODD);
bottomBorderPath.moveTo(rbo.x, rbo.y);
bottomBorderPath.lineTo(lbo.x, lbo.y);
@ -508,10 +559,11 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
}
if (this.borderLeftWidth > 0) {
Paint leftBorderPaint = new Paint();
leftBorderPaint.reset();
leftBorderPaint.setColor(this.borderLeftColor);
leftBorderPaint.setAntiAlias(true);
Path leftBorderPath = new Path();
leftBorderPath.reset();
leftBorderPath.setFillType(Path.FillType.EVEN_ODD);
leftBorderPath.moveTo(lbo.x, lbo.y);
leftBorderPath.lineTo(lto.x, lto.y);
@ -606,7 +658,12 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
top = cY - rY;
right = (rX * 2) + left;
bottom = (rY * 2) + top;
canvas.drawOval(new RectF(left, top, right, bottom), paint);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawOval(left, top, right, bottom, paint);
} else {
canvas.drawOval(new RectF(left, top, right, bottom), paint);
}
break;
case "polygon":
Path path = new Path();
@ -832,18 +889,31 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
return drawable;
}
private final Path outlineBackgroundPath = new Path();
private final RectF outlineRectF = new RectF();
@Override
public void getOutline(@NonNull Outline outline) {
if (android.os.Build.VERSION.SDK_INT >= 21) {
Path backgroundPath = new Path();
outlineBackgroundPath.reset();
float[] backgroundRadii = {
Math.max(0, borderTopLeftRadius), Math.max(0, borderTopLeftRadius),
Math.max(0, borderTopRightRadius), Math.max(0, borderTopRightRadius),
Math.max(0, borderBottomRightRadius), Math.max(0, borderBottomRightRadius),
Math.max(0, borderBottomLeftRadius), Math.max(0, borderBottomLeftRadius)
};
backgroundPath.addRoundRect(new RectF(getBounds()), backgroundRadii, Path.Direction.CW);
outline.setConvexPath(backgroundPath);
outlineRectF.setEmpty();
outlineRectF.set(getBounds());
backgroundPath.addRoundRect(outlineRectF, backgroundRadii, Path.Direction.CW);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// see setConvexPath notes
outline.setPath(backgroundPath);
} else {
outline.setConvexPath(backgroundPath);
}
} else {
throw new IllegalStateException("Method supported on API 21 or higher");
}

View File

@ -8,10 +8,14 @@ import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatDelegate;
import org.nativescript.widgets.image.BitmapOwner;
@ -26,6 +30,7 @@ public class ImageView extends androidx.appcompat.widget.AppCompatImageView impl
private final Path path = new Path();
private final RectF rect = new RectF();
private final Paint paint = new Paint();
private double scaleW = 1;
private double scaleH = 1;
@ -209,6 +214,8 @@ public class ImageView extends androidx.appcompat.widget.AppCompatImageView impl
}
}
private boolean mSettingBitmap = false;
@Override
public void setImageBitmap(Bitmap bm) {
Fetcher fetcher = Fetcher.getInstance(this.getContext());
@ -217,11 +224,75 @@ public class ImageView extends androidx.appcompat.widget.AppCompatImageView impl
if (mUseCache && mUri != null && mBitmap != null && fetcher != null) {
fetcher.removeBitmap(mUri);
}
mSettingBitmap = true;
super.setImageBitmap(bm);
this.mBitmap = bm;
if (bm != null) {
bitmapWidth = bm.getWidth();
bitmapHeight = bm.getHeight();
bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
} else {
bitmapWidth = -1;
bitmapHeight = -1;
}
mSettingBitmap = false;
}
@Override
public void setImageDrawable(@Nullable Drawable drawable) {
super.setImageDrawable(drawable);
setBitmapShader();
}
private final Canvas canvas = new Canvas();
private BitmapShader bitmapShader = null;
private int bitmapWidth = -1;
private int bitmapHeight = -1;
private void setBitmapShader() {
if (mSettingBitmap) {
return;
}
Bitmap mBitmap = null;
Drawable drawable = getDrawable();
if (drawable != null) {
if (drawable instanceof BitmapDrawable) {
mBitmap = ((BitmapDrawable) drawable).getBitmap();
} else {
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
canvas.setBitmap(bitmap);
Rect previousBounds = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
previousBounds = drawable.getBounds();
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
}
drawable.draw(canvas);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
drawable.setBounds(previousBounds);
}
mBitmap = bitmap;
}
} else {
bitmapShader = null;
}
if (mBitmap != null) {
bitmapWidth = mBitmap.getWidth();
bitmapHeight = mBitmap.getHeight();
bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
} else {
bitmapWidth = -1;
bitmapHeight = -1;
}
}
@Override
protected void onDraw(Canvas canvas) {
BorderDrawable background = this.getBackground() instanceof BorderDrawable ? (BorderDrawable) this.getBackground() : null;
@ -257,17 +328,21 @@ public class ImageView extends androidx.appcompat.widget.AppCompatImageView impl
innerHeight = this.getHeight() - borderTopWidth - borderBottomWidth;
// TODO: Capture all created objects here in locals and update them instead...
Path path = new Path();
path.reset();
paint.reset();
float[] radii = {
Math.max(0, borderTopLeftRadius - borderLeftWidth), Math.max(0, borderTopLeftRadius - borderTopWidth),
Math.max(0, borderTopRightRadius - borderRightWidth), Math.max(0, borderTopRightRadius - borderTopWidth),
Math.max(0, borderBottomRightRadius - borderRightWidth), Math.max(0, borderBottomRightRadius - borderBottomWidth),
Math.max(0, borderBottomLeftRadius - borderLeftWidth), Math.max(0, borderBottomLeftRadius - borderBottomWidth)
};
path.addRoundRect(new RectF(borderLeftWidth, borderTopWidth, borderLeftWidth + innerWidth, borderTopWidth + innerHeight), radii, Path.Direction.CW);
Paint paint = new Paint();
BitmapShader bitmapShader = new BitmapShader(this.mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
rect.setEmpty();
rect.set(borderLeftWidth, borderTopWidth, borderLeftWidth + innerWidth, borderTopWidth + innerHeight);
path.addRoundRect(rect, radii, Path.Direction.CW);
float bitmapWidth = (float) mBitmap.getWidth();
float bitmapHeight = (float) mBitmap.getHeight();