1

I have a small program that takes a Chair.jpg and converts it a bitmap. The reason for this is to change the color type of the pixels to the format of BGR_888 (which I got from this stack overflow post.)

However the bitmap is null. I believe it is because of this reason D/skia: --- Failed to create image decoder with message 'unimplemented'. Looking on online, it maybe because I need to compress this? Im not sure. Can someone help me understand?

public class MainActivity extends AppCompatActivity {

    TextView textView;
    ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = findViewById(R.id.imageView);
        textView = findViewById(R.id.textView);

        Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.chair);
        imageView.setImageBitmap(b);

        int width = b.getWidth();
        int height = b.getHeight();

        Log.d("bitmap height", String.valueOf(height));
        Log.d("bitmap width", String.valueOf(width));
        Bitmap.Config config = b.getConfig();
        Log.d("b color format", String.valueOf(config));

        byte[] newImage = getImagePixels(b);
        Log.d("getImagePixels Result", String.valueOf(newImage));

        boolean b2 = isNewBitmapNull(newImage);
        Log.d("is my new bitmap null:",String.valueOf(b2));


    }

    /// <summary>
    /// function getImagePixels
    /// Purpose: Given the bitmap image return the converted 4 byte ARGB to 3 byte BGR
    /// </summary>
    /// <param name="image"> Bitmap image object </param>

    public byte[] getImagePixels(Bitmap image) {
        // calculate how many bytes our image consists of
        int bytes = image.getByteCount();
        // Create a new buffer
        ByteBuffer buffer = ByteBuffer.allocate(bytes);
        // Move the byte data to the buffer
        image.copyPixelsToBuffer(buffer);

        // Get the underlying array containing the data.
        byte[] temp = buffer.array();

        // Allocate for 3 byte BGR
        byte[] pixels = new byte[(temp.length / 4) * 3];

        // Copy pixels into place
        for (int i = 0; i < (temp.length / 4); i++) {
            pixels[i * 3] = temp[i * 4 + 3];     // B
            pixels[i * 3 + 1] = temp[i * 4 + 2]; // G
            pixels[i * 3 + 2] = temp[i * 4 + 1]; // R

            // Alpha is discarded
        }
        Log.d("check if it is array", String.valueOf(pixels.getClass().isArray()));
        Log.d("array object type", String.valueOf(pixels.getClass()));
        Log.d("array length", String.valueOf(pixels.length));
        return pixels;
    }

    public static byte[] convertBitmapToByteArrayUncompressed(Bitmap bitmap){
        ByteBuffer byteBuffer = ByteBuffer.allocate(bitmap.getByteCount());
        bitmap.copyPixelsToBuffer(byteBuffer);
        byteBuffer.rewind();
        return byteBuffer.array();
    }

    /// <summary>
    /// function copyPixelsToBitmap
    /// Purpose: Given the pixel data return a bitmap of size [?,?],PixelFormat=24BGR
    /// </summary>
    /// <param name="pixels"> Byte array with pixel data </param>
    public Bitmap copyPixelsToBitmap(byte[] pixels){
        //Here create the Bitmap to the know height, width and format
        Bitmap bmp = BitmapFactory.decodeByteArray(pixels, 0, pixels.length);

        //Return the bitmap
        return bmp;
    }

    /// <summary>
    /// function isNewBitmapNull
    /// Purpose: Given the pixel data return T/F is the bitmap was created
    /// </summary>
    /// <param name="pixels"> Byte array with pixel data </param>
    public boolean isNewBitmapNull(byte[] pixels){
        //BitmapFactory.Options options = new BitmapFactory.Options();
        //options.inMutable = true;
        Bitmap bmp = BitmapFactory.decodeByteArray(pixels, 0, pixels.length);
        if (bmp == null)
            return true;


        return false;
    }
}

For the isNewBitMapNull method, I have also tried adding the BitMapFactory options but still get a null bitmap:

//BitmapFactory.Options options = new BitmapFactory.Options();
//options.inMutable = true;
Bitmap bmp = BitmapFactory.decodeByteArray(pixels, 0, pixels.length, options);

Here is the output:

D/bitmap height: 1260 
D/bitmap width: 1260
D/b color format: ARGB_8888
D/check if it is array: true
D/array object type: class [B
D/array length: 4762800
D/getImagePixels Result: [B@5a36e09
D/skia: --- Failed to create image decoder with message 'unimplemented'
D/is my new bitmap null:: true
fdermishin
  • 3,519
  • 3
  • 24
  • 45
Nelly Yuki
  • 399
  • 1
  • 4
  • 16
  • Your conversion to BGR is either wrong or depends on endianness of the platform, as `temp[i * 4 + 3]` returns alpha channel in my case. – fdermishin Jan 24 '21 at 09:10

1 Answers1

2

It seems that you are trying to create a bitmap that has 3 bytes per pixel, but it is not possible in Android since Bitmap supports 1, 2, 4 or 8 bytes per pixel.

That is why you need to create a new bitmap with the pixel format that is supported. But using BitmapFactory.decodeByteArray doesn't work because it creates a bitmap from compressed image data, such as data that is stored in .jpg file. But you have array of raw pixel values (uncompressed).

While compressing the image and then reading it using decodeByteArray is an option, you will need to implement compression of BGR_888 yourself or use a 3rd party library. The simpler way is to convert BGR_888 back to ARGB_8888 and create a bitmap from this data:

val temp = IntArray(width * height)

for (i in 0 until width * height) {
    val red = pixels[i * 3 + 2].toInt() and 0xff
    val green = pixels[i * 3 + 1].toInt() and 0xff
    val blue = pixels[i * 3].toInt() and 0xff
    temp[i] = Color.rgb(red, green, blue)
}
return Bitmap.createBitmap(temp, width, height, Bitmap.Config.ARGB_8888)

Java version:

int[] temp = new int[width * height];

for (int i = 0; i < width * height; ++i) {
    int red = (int)pixels[i * 3 + 2] & 0xff;
    int green = (int)pixels[i * 3 + 1] & 0xff;
    int blue = (int)pixels[i * 3] & 0xff;
    temp[i] = Color.rgb(red, green, blue);
}
return Bitmap.createBitmap(temp, width, height, Bitmap.Config.ARGB_8888);
fdermishin
  • 3,519
  • 3
  • 24
  • 45
  • Hi @fdermishin :) I kind of understand what you are saying. I actually need BGR_888 format instead of ARGB_8888 :P So for this to work, I need to compress my array of raw pixels? and then send that to the bitmap? (tho you mention its not possible since its 3 BPP and not 4 BPP for example)...what are the options then? – Nelly Yuki Jan 24 '21 at 20:02
  • It depends on what you are trying to achieve. How are you going to use BGR_888 format? Are you going to process the array of BGR_888 pixels and then use `Bitmap` class to display the image? Or does some library accepts only bitmaps that are in BGR_888 format? Or maybe you are trying to reduce memory consumption of the bitmap? – fdermishin Jan 24 '21 at 20:26
  • Pixel format is just an internal implementation detail, and both BGR_888 and ARGB_8888 (with alpha equal to 1) store the same information, so everything that could be done with one format is possible to do with the other. So, unless there are specific requirements that depend on the internal details of the bitmap, pixel format doesn't matter. – fdermishin Jan 24 '21 at 20:33
  • Thanks for that info @fdermishin I am using a custom object detection library where the channel order needs to BGR. – Nelly Yuki Jan 24 '21 at 22:49
  • Ultimately, the jpeg image needs to be converted to BGR_888 format for the library to work. That is why turned the jpeg image into a bitmap and then a byte array. So after I have the raw pixels and make it so its visible, I can display the image. – Nelly Yuki Jan 24 '21 at 23:21
  • What is the type of bitmap argument that library accepts? Is it byte array? Are you going to display the image using the library itself? – fdermishin Jan 24 '21 at 23:29
  • the parameter that the library expects is a data buffer pointer to BGR888 image. Im not sure if I was headed in the right direction using the bitmap, it just happened to be the way I found to convert the image to that format of BGR888. So I thought using a bitmap to put the raw pixels together could make it into an image...if that makes sense... – Nelly Yuki Jan 25 '21 at 01:01
  • It seems that `Bitmap` is not needed in your case. Byte array can be converted to `ByteBuffer` and then can be passed into the library. – fdermishin Jan 25 '21 at 02:27
  • Okay! I will attempt to do it this way then and see if it works :) Thanks for the helpful comments!! @fdermishin Just out of curiosity, if I did want to display the BGR version of the image, how might I go about doing that? Is it like you said early, to write my own compression function? – Nelly Yuki Jan 25 '21 at 02:31