Android:位图转换为RGBA再转回

5

我正在尝试编写一些方法,将Android位图转换为RGBA字节数组,然后再将其转换回位图。问题在于,我似乎没有找到正确的公式,因为颜色总是出错。我尝试了几种不同的假设,但都无济于事。

所以,这是从位图转换为RGBA的方法,我认为是正确的:

public static byte[] bitmapToRgba(Bitmap bitmap) {
    int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
    byte[] bytes = new byte[pixels.length * 4];
    bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
    int i = 0;
    for (int pixel : pixels) {
        // Get components assuming is ARGB
        int A = (pixel >> 24) & 0xff;
        int R = (pixel >> 16) & 0xff;
        int G = (pixel >> 8) & 0xff;
        int B = pixel & 0xff;
        bytes[i++] = (byte) R;
        bytes[i++] = (byte) G;
        bytes[i++] = (byte) B;
        bytes[i++] = (byte) A;
    }
    return bytes;
}

这是一种从这些字节中创建位图的方法,但效果并不如预期:

public static Bitmap bitmapFromRgba(int width, int height, byte[] bytes) {
    int[] pixels = new int[bytes.length / 4];
    int j = 0;

    // It turns out Bitmap.Config.ARGB_8888 is in reality RGBA_8888!
    // Source: https://dev59.com/rafja4cB1Zd3GeqP0sS3#47982505
    // Now, according to my own experiments, it seems it is ABGR... this sucks.
    // So we have to change the order of the components

    for (int i = 0; i < pixels.length; i++) {
        byte R = bytes[j++];
        byte G = bytes[j++];
        byte B = bytes[j++];
        byte A = bytes[j++];

        int pixel = (A << 24) | (B << 16) | (G << 8) | R;
        pixels[i] = pixel;
    }

    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    bitmap.copyPixelsFromBuffer(IntBuffer.wrap(pixels));
    return bitmap;
}

这是我最后的实现,尽管我尝试了几种不同的方法都没有成功。我假设createBitmap期望ABGR,尽管指定了ARGB_8888,因为我已经进行了硬编码像素的实验,例如:

0xff_ff_00_00 -> got blue 
0xff_00_ff_00 -> got green
0xff_00_00_ff -> got red

无论如何,也许这种假设是错误的,可能是之前某个错误的假设的结果。

我认为主要问题可能与使用有符号数字值有关,因为 Java 中没有无符号数(好吧,在 Java 8+ 中有一些东西,但一方面我认为不必使用这些东西,另一方面它也不受我需要支持的旧 Android 版本支持)。

非常感谢您的任何帮助。

提前感谢您!


似乎颜色分量顺序错误的问题与copyPixelsFromBuffer有关。也许一切都归结于这个。我已经用setPixels替换了它,结果不同了。是与小/大端相关的问题吗? - Fran Marzoa
1个回答

7

我自己解决了。有很多问题,但是所有这些问题都始于这一行:

bitmap.copyPixelsFromBuffer(IntBuffer.wrap(pixels));

看起来颜色分量混合的方式有误。可能与字节顺序(小/大端)有关,无论如何,我用setPixels绕过了这个问题:

bitmap.setPixels(pixels, 0, width, 0, 0, width, height); 

这是最终能正常工作的代码,如果对其他人有用的话,这里提供给大家参考:

public static byte[] bitmapToRgba(Bitmap bitmap) {
    if (bitmap.getConfig() != Bitmap.Config.ARGB_8888)
        throw new IllegalArgumentException("Bitmap must be in ARGB_8888 format");
    int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
    byte[] bytes = new byte[pixels.length * 4];
    bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
    int i = 0;
    for (int pixel : pixels) {
        // Get components assuming is ARGB
        int A = (pixel >> 24) & 0xff;
        int R = (pixel >> 16) & 0xff;
        int G = (pixel >> 8) & 0xff;
        int B = pixel & 0xff;
        bytes[i++] = (byte) R;
        bytes[i++] = (byte) G;
        bytes[i++] = (byte) B;
        bytes[i++] = (byte) A;
    }
    return bytes;
}

public static Bitmap bitmapFromRgba(int width, int height, byte[] bytes) {
    int[] pixels = new int[bytes.length / 4];
    int j = 0;

    for (int i = 0; i < pixels.length; i++) {
        int R = bytes[j++] & 0xff;
        int G = bytes[j++] & 0xff;
        int B = bytes[j++] & 0xff;
        int A = bytes[j++] & 0xff;

        int pixel = (A << 24) | (R << 16) | (G << 8) | B;
        pixels[i] = pixel;
    }


    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
    return bitmap;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接