How to save Image of format Image.Format.RGB565 as a Bitmap

I’m currently trying to save an image (e.g. from a Texture) to the sd card for example, here is the code I use, I think it will explain it best. I used some of the methods from AndroidScreenshots.java and JmeAndroidSystem.java and just copied them for easier testing:

[java]
Image image = an image like new Image(Image.Format.RGB565, (int) width, (int) height, pixels);

FileOutputStream outStream = new FileOutputStream(someTargetFileDestination);
ByteBuffer imagePixels = image.getData(0);
writeImageFile(outStream, imagePixels, image.getWidth(),
image.getHeight());

private void writeImageFile(FileOutputStream outStream,
ByteBuffer imageData, int width, int height) {
Bitmap bitmapImage = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
convertScreenShot(imageData, bitmapImage);
bitmapImage.compress(Bitmap.CompressFormat.PNG, 95, outStream);
}

public static void convertScreenShot(ByteBuffer buf, Bitmap bitmapImage) {
int width = bitmapImage.getWidth();
int height = bitmapImage.getHeight();
int size = width * height;

// Grab data from ByteBuffer as Int Array to manipulate data and send to
// image
IntBuffer intBuffer = buf.asIntBuffer();

Log.w(LOGTAG, “width=” + width);
Log.w(LOGTAG, “height=” + height);
Log.w(LOGTAG, “imagesize=” + size);
Log.w(LOGTAG, “intBuffer.capacity()=” + intBuffer.capacity());
int[] data = new int[size];
intBuffer.get(data, 0, data.length);
// intBuffer.get(data);

// convert from GLES20.GL_RGBA to Bitmap.Config.ARGB_8888
// ** need to swap RED and BLUE **
if (bitmapImage.getConfig() == Bitmap.Config.ARGB_8888) {
	for (int idx = 0; idx < data.length; idx++) {
		int initial = data[idx];
		int pb = (initial >> 16) & 0xff;
		int pr = (initial << 16) & 0x00ff0000;
		int pix1 = (initial & 0xff00ff00) | pr | pb;
		data[idx] = pix1;
	}
}

// OpenGL and Bitmap have opposite starting points for Y axis (top vs
// bottom)
// Need to write the data in the image from the bottom to the top
// Use size-width to indicate start with last row and increment by
// -width for each row
bitmapImage.setPixels(data, size – width, -width, 0, 0, width, height);

}
[/java]

When i try to use this the image is not stored because the intBuffer.capacity is half the size of the imagesize (I think because the Image has the format Image.Format.RGB565 and the bitmap has the format Bitmap.Config.ARGB_8888. Here are the log outputs i added in the code:

width=640
height=480
imagesize=307200
intBuffer.capacity()=153600

so my question is how to I convert the Image format or how do i fill the data array correctly when I get an Image with format Image.Format.RGB565 ?

formatted the source code

@simon.heinen said: ups here the code in a formatted way: (where did the edit button go?)

add /edit to the url

1 Like

Ok i think I get it now, 1 int value are 2 grb565 values (which were before stored in 4 byte values) so i have to make a second int array 4 times that big and extract the 4 color values from the one int. lets see if this works

Ok since it took be some time I want to post the code here, maybe it will help someone in the future:

[java]

/**

  • converts an {@link Image} to a {@link Bitmap} which can then be stored. This

  • class has Android dependencies
    */
    public class TextureToBitmapLogic {

    private static final String LOG_TAG = “TextureToBitmapLogic”;

    public void saveImageToFile(File file, Image imageRGB565) {
    saveToFile(file, imageRGB565.getData(0), imageRGB565.getWidth(),
    imageRGB565.getHeight());
    }

    public void saveToFile(final File file, ByteBuffer rgb565Bytes, int width,
    int height) {
    try {
    FileOutputStream outStream = new FileOutputStream(file);
    Bitmap bitmapImage = rgb565BufferToBitmap(rgb565Bytes, width,
    height);
    bitmapImage.compress(Bitmap.CompressFormat.PNG, 95, outStream);
    outStream.close();
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    public Bitmap rgb565BufferToBitmap(ByteBuffer image, int width, int height) {
    // Image i = camImgPreview.getCameraPrefImageRGB565();
    // ByteBuffer image = i.getData(0);

     byte[] a = new byte[image.capacity()];
     image.get(a, 0, a.length);
     Log.i(LOG_TAG, "will create image with length=" + a.length);
     Bitmap targetImage = Bitmap.createBitmap(width, height,
     		Bitmap.Config.ARGB_8888);
     Log.i(LOG_TAG, "   > bitmap.width=" + width);
     Log.i(LOG_TAG, "   > bitmap.height=" + height);
     Log.i(LOG_TAG, "   > width * height=" + width * height);
     targetImage.setPixels(RGBConversion.toArgb8888IntArray(image), 0,
     		width, 0, 0, width, height);
     return targetImage;
    

    }

}

/**

  • wrapper for rgb565 to argb8888 conversion. (pure java)
    */
    public class RGBConversion {

    public static void main(String[] args) {
    // http://www.mathsisfun.com/binary-decimal-hexadecimal-converter.html
    short c565 = (short) 0x7E0; // nearly full red in 565
    System.out.println(“c=” + asBinaryString(c565));
    int c8888 = toArgb8888(c565);
    System.out.println(“color=” + asBinaryString(c8888));
    }

    /**

    • @param byteBuffer

    •        has to be rgb565
      
    • @param targetImage

    • @return
      */
      public static int[] toArgb8888IntArray(ByteBuffer byteBuffer) {

      ArrayList<Integer> ints = new ArrayList<Integer>();

      byteBuffer.rewind();
      while (byteBuffer.hasRemaining()) {
      int fourBytes = little2big(byteBuffer.getInt());
      ints.add(fourBytes);
      }

      ArrayList<Integer> pixelArrayList = new ArrayList<Integer>();
      for (int i = 0; i < ints.size(); i++) {
      int twoRgb565Colors = ints.get(i);
      short c1 = (short) (twoRgb565Colors & 0xffff);
      short c2 = (short) ((twoRgb565Colors >> 16) & 0xffff);
      pixelArrayList.add(toArgb8888(c1));
      pixelArrayList.add(toArgb8888(c2));
      }
      int[] pixelArray = new int[pixelArrayList.size()];
      for (int i = 0; i < pixelArray.length; i++) {
      pixelArray[i] = pixelArrayList.get(i);
      }
      return pixelArray;
      }

    private static final ColorRGBA conversionHelperColor = new ColorRGBA();

    /**

    • @param rgb565
    •        rgb565 color (two bytes)
      
    • @return argb8888 color int
      */
      private static int toArgb8888(short rgb565) {
      // r r r r r | g g g g g g | b b b b b
      float red = ((rgb565 >> 11) & 0x1F); // 0x1F = 11111
      float green = ((rgb565 >> 5) & 0x3F); // 0x3F= 111111
      float blue = (rgb565 & 0x1F);
      conversionHelperColor.set(red / 31f, green / 63f, blue / 31f, 1f);
      return conversionHelperColor.asIntARGB();
      }

    private static int little2big(int i) {
    return ((i & 0xff ) << 24 ) + ( ( i & 0xff00 ) << 8 ) + ( (i & 0xff0000 ) >> 8 )
    + ( (i >> 24 ) & 0xff);
    }

    private static String asBinaryString(short s) {
    return Integer.toBinaryString(0xFFFF & s);
    }

    private static String asBinaryString(int i) {
    return Integer.toBinaryString(0xFFFFFFFF & i);
    }

}

[/java]

1 Like