最近在做公司项目时,需要对 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 中我们可以使用 copyPixelsToBuffercopyPixelsFromBuffer 来加速像素操作。

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

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 和相应的透明度,然后就可以针对当前的颜色做统计或者是批量转换操作了。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Logan 微信支付

微信支付

Logan 支付宝

支付宝

Logan 贝宝

贝宝