最近在做公司项目时,需要对 bitmap 重新计算像素点颜色,在
onDraw()中处理时,记录了以下方法。
# setPixel 方法
我们首先遍历 bitmap 所有的像素点,通过 getPixel() 获取当前像素点的颜色,然后根据特定算法,计算出最终应该展示的实际颜色。
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 中我们可以使用 copyPixelsToBuffer 和 copyPixelsFromBuffer 来加速像素操作。
查看源码,我们看到最主要的其实是这个方法:
nativeCopyPixelsToBuffer(mNativePtr, dst); |
private static native void nativeCopyPixelsToBuffer(long nativeBitmap, | |
Buffer dst); |
它是一个 native 修饰的方法,即 JNI 调用的方法,使用底层调用效率更高。
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 采用的是以下方法:
Bitmap compositeBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); |
我们使用了 ARGB_8888,每个像素占了 4 位,可以通过下面方法获取数组大小:
bitmap.getWidth() * bitmap.getHeight() * 4 |
循环遍历后取到 RGB 和相应的透明度,然后就可以针对当前的颜色做统计或者是批量转换操作了。