之前有处理过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; } Integer area = mTrackPointAreaMap.get(rollPass); if (area == null) { area = 0; } area++; mTrackPointAreaMap.put(rollPass, area); int newColor = RollingMonitorUtil.GetMappingColor(mCurrentMode == MODE_ROLLING ? mMaxRollPass : mMaxVibrateRollPass, rollPass); compositeBitmap.setPixel(i, j, newColor); } }
|
在像素较多时,这个方法会比较耗时,会有性能问题。
copyPixelsToBuffer方法
在C#中有LockBits方法加快速度,在Android中我们可以使用copyPixelsToBuffer和copyPixelsFromBuffer来加速像素操作。
查看源码,我们看到最主要的其实是这个方法:
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) { 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; } 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]; }
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.