Bitmap操作像素 2

  • ~5.78K 字

之前有处理过bitmap重新计算像素点颜色,之前采用的copyPixelsToBuffer方法替换setPixel,效率提升很大,但是最近AI给我提供了一个新方法,更快了。

setPixel方法

我们首先遍历bitmap所有的像素点,通过getPixel()获取当前像素点的颜色,然后根据特定算法,计算出最终应该展示的实际颜色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
for (int i = y1; i < y2; i++) {
for (int j = x2; j < x1; j++) {
int oldColor = compositeBitmap.getPixel(i, j);
int alpha = Color.alpha(oldColor);
int defaultAlpha = Color.alpha(defaultColor);
if (alpha == 0) {
continue;
}
int rollPass = Math.round(((float) (alpha / defaultAlpha)));
if (rollPass == 0) {
continue;
}
if (rollPass > mMaxRollPass) {
rollPass = mMaxRollPass;
}
//region 计算面积
Integer area = mTrackPointAreaMap.get(rollPass);
if (area == null) {
area = 0;
}
area++;
mTrackPointAreaMap.put(rollPass, area);
//endregion
int newColor = RollingMonitorUtil.GetMappingColor(mCurrentMode == MODE_ROLLING ? mMaxRollPass : mMaxVibrateRollPass, rollPass);
compositeBitmap.setPixel(i, j, newColor);
}
}

在像素较多时,这个方法会比较耗时,会有性能问题。

copyPixelsToBuffer方法

在C#中有LockBits方法加快速度,在Android中我们可以使用copyPixelsToBuffercopyPixelsFromBuffer来加速像素操作。

查看源码,我们看到最主要的其实是这个方法:

1
nativeCopyPixelsToBuffer(mNativePtr, dst);
1
2
private static native void nativeCopyPixelsToBuffer(long nativeBitmap,
Buffer dst);

它是一个native修饰的方法,即JNI调用的方法,使用底层调用效率更高。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int size = compositeBitmap.getRowBytes() * compositeBitmap.getHeight() * 4;
ByteBuffer buf = ByteBuffer.allocate(size);
compositeBitmap.copyPixelsToBuffer(buf);
byte[] byt = buf.array();
int defaultAlpha = Color.alpha(defaultColor);
for (int ctr = 0; ctr < size; ctr += 4) {
//access array in form of argb. for ex. byt[0] is 'r', byt[1] is 'g' and so on..
int r = byt[ctr] & 0xff;
int g = byt[ctr + 1] & 0xff;
int b = byt[ctr + 2] & 0xff;
int a = byt[ctr + 3] & 0xff;
if (a == 0) {
continue;
}
int rollPass = Math.round(((float) (a / defaultAlpha)));
if (rollPass == 0) {
continue;
}
int maxRollPass = mCurrentMode == MODE_ROLLING ? mMaxRollPass : mMaxVibrateRollPass;
if (rollPass > maxRollPass) {
rollPass = maxRollPass;
}
//region 计算面积
Integer area = mTrackPointAreaMap.get(rollPass);
if (area == null) {
area = 0;
}
area++;
mTrackPointAreaMap.put(rollPass, area);
int newColor = mCurrentMode == MODE_ROLLING ? mRollColorPalette[rollPass].getColor() : mVibrateColorPalette[rollPass].getColor();
byt[ctr] = (byte) Color.red(newColor);
byt[ctr + 1] = (byte) Color.green(newColor);
byt[ctr + 2] = (byte) Color.blue(newColor);
byt[ctr + 3] = (byte) Color.alpha(newColor);
}
ByteBuffer retBuf = ByteBuffer.wrap(byt);
compositeBitmap.copyPixelsFromBuffer(retBuf);

因为我构建的bitmap采用的是以下方法:

1
Bitmap compositeBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);

我们使用了ARGB_8888,每个像素占了4位,可以通过下面方法获取数组大小:

1
bitmap.getWidth() * bitmap.getHeight() * 4

循环遍历后取到RGB和相应的透明度,然后就可以针对当前的颜色做统计或者是批量转换操作了。

setPixelf方法进阶版

减少在循环中的计算操作,通过预生成的调色板,优化整数运算代替浮点运算,降低计算复杂度,减少计算量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
int width = compositeBitmap.getWidth();
int height = compositeBitmap.getHeight();
int[] pixels = new int[width * height];

// 获取像素数据
compositeBitmap.getPixels(pixels, 0, width, 0, 0, width, height);

// 确定当前模式参数
int maxRollPass = (mCurrentMode == MODE_VIBRATE) ? mMaxVibrateRollPass : mMaxRollPass;
int defaultAlpha = Color.alpha(defaultColor);

// 预生成颜色调色板
int[] colorPalette = new int[maxRollPass + 1];
if (mVibrateColorPalette != null && mRollColorPalette != null) {
for (int rp = 0; rp <= maxRollPass; rp++) {
int color = (mCurrentMode == MODE_VIBRATE)
? mVibrateColorPalette[rp].getColor()
: mRollColorPalette[rp].getColor();
colorPalette[rp] = color;
}
}

// 使用数组进行面积统计
int[] areaCounts = new int[maxRollPass + 1];
int halfDefaultAlpha = defaultAlpha / 2;

// 处理每个像素
for (int i = 0; i < pixels.length; i++) {
int alpha = (pixels[i] >> 24) & 0xFF;
if (alpha == 0) continue;

// 优化整数运算代替浮点运算
int rollPass = (alpha + halfDefaultAlpha) / defaultAlpha;
if (rollPass == 0) continue;

// 限制最大遍数
if (rollPass > maxRollPass) rollPass = maxRollPass;

// 统计面积
areaCounts[rollPass]++;

// 直接使用预生成的颜色
pixels[i] = colorPalette[rollPass];
}

// 转换统计结果到Map
for (int rp = 0; rp < areaCounts.length; rp++) {
if (areaCounts[rp] > 0) {
mTrackPointAreaMap.put(rp, areaCounts[rp]);
}
}

// 写回修改后的像素数据
compositeBitmap.setPixels(pixels, 0, width, 0, 0, width, height);

通过上述调整,可以将绘制时间从900ms 缩减到 180ms.

赞助喵
非常感谢您的喜欢!
赞助喵
分享这一刻
让朋友们也来瞅瞅!