Android Canvas drawBitmap 尽量使用Matrix缩放 原创
测试代码
private void testDrawBitmap() {
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
int dstWidth = 192 * 9, dstHeight = 192 * 9;
Bitmap desBitmap = Bitmap.createBitmap(dstWidth, dstHeight, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(desBitmap);
Paint paint = new Paint();
//前者
long time0 = SystemClock.elapsedRealtime();
Matrix mMatrix = new Matrix();
mMatrix.postScale((float) dstWidth / srcBitmap.getWidth(),
(float) dstHeight / srcBitmap.getHeight());
canvas.drawBitmap(srcBitmap, mMatrix, paint);
Logger.d("Matrix time:" + (SystemClock.elapsedRealtime() - time0));
//后者
time0 = SystemClock.elapsedRealtime();
canvas.drawBitmap(srcBitmap,
new Rect(0, 0, srcBitmap.getWidth(), srcBitmap.getHeight()),
new Rect(0, 0, dstWidth, dstHeight), paint);
Logger.d("Rect time:" + (SystemClock.elapsedRealtime() - time0));
srcBitmap.recycle();
desBitmap.recycle();
}
测试结果
前者Matrix (22ms)比后者Rect(35ms)快,性能越差的设备越明显
测试结论
尽量用Matrix替换Rect
drawBitmap源码跟踪
通过8.0源码可以找到 public class Canvas extends BaseCanvas
在BaseCanvas有如下代码
public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
throwIfHasHwBitmapInSwMode(paint);
nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.getNativeInstance(), matrix.ni(),
paint != null ? paint.getNativeInstance() : 0);
}
public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst,
@Nullable Paint paint) {
...
nDrawBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance(), left, top, right, bottom,
dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
bitmap.mDensity);
}
最终调用在 frameworks/base/core/jni/android_graphics_Canvas.cpp,其中有如下代码片段
static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
jlong matrixHandle, jlong paintHandle) {
const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap);
get_canvas(canvasHandle)->drawBitmap(bitmap, *matrix, paint);
}
static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
float srcLeft, float srcTop, float srcRight, float srcBottom,
float dstLeft, float dstTop, float dstRight, float dstBottom,
jlong paintHandle, jint screenDensity, jint bitmapDensity) {
Canvas* canvas = get_canvas(canvasHandle);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap);
if (screenDensity != 0 && screenDensity != bitmapDensity) {
Paint filteredPaint;
if (paint) {
filteredPaint = *paint;
}
filteredPaint.setFilterQuality(kLow_SkFilterQuality);
canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
} else {
canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
dstLeft, dstTop, dstRight, dstBottom, paint);
}
}
即最终分别调用了
canvas->drawBitmap(bitmap, *matrix, paint);
canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom, dstLeft, dstTop, dstRight, dstBottom, paint);
在frameworks/base/libs/hwui/hwui/Canvas.cpp有如下代码
Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) {
if (uirenderer::Properties::isSkiaEnabled()) {
return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
}
return new uirenderer::RecordingCanvas(width, height);
}
因此Canvas就是是RecordingCanvas,在 frameworks/base/libs/hwui/RecordingCanvas.cpp有如下代码
void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix,
const SkPaint* paint) {
if (matrix.isIdentity()) {
drawBitmap(bitmap, paint);
} else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask))
&& MathUtils::isPositive(matrix.getScaleX())
&& MathUtils::isPositive(matrix.getScaleY())) {
// SkMatrix::isScaleTranslate() not available in L
SkRect src;
SkRect dst;
bitmap.getBounds(&src);
matrix.mapRect(&dst, src);
drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
} else {
save(SaveFlags::Matrix);
concat(matrix);
drawBitmap(bitmap, paint);
restore();
}
}
void RecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint) {
if (srcLeft == 0 && srcTop == 0
&& srcRight == bitmap.width()
&& srcBottom == bitmap.height()
&& (srcBottom - srcTop == dstBottom - dstTop)
&& (srcRight - srcLeft == dstRight - dstLeft)) {
// transform simple rect to rect drawing case into position bitmap ops, since they merge
save(SaveFlags::Matrix);
translate(dstLeft, dstTop);
drawBitmap(bitmap, paint);
restore();
} else {
addOp(alloc().create_trivial<BitmapRectOp>(
Rect(dstLeft, dstTop, dstRight, dstBottom),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
refPaint(paint), refBitmap(bitmap),
Rect(srcLeft, srcTop, srcRight, srcBottom)));
}
}
本文来自博客园,作者:清霜辰,转载请注明原文链接:https://www.cnblogs.com/cnjim/p/18443525
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了