androidbitmapeclipse-mat

How to actually see a Bitmap taken from an Android heap dump


In the process of tracking severe memory issues in my app, I looked at several heap dumps from my app, and most of the time I have a HUGE bitmap that I don't know of.

It takes 9.4MB, or 9,830,400 bytes, or actually a 1280x1920 image at 4 bytes per pixels.

I checked in Eclipse MAT, it is indeed a byte[9830400], that has one incoming reference which is a android.graphics.Bitmap.

I'd like to dump this to a file and try to see it. I can't understand where is it coming from. My biggest image in all my drawables is a 640x960 png, which takes less than 3MB.

I tried to use Eclipse to "copy value to file", but I think it simply prints the buffer to the file, and I don't know any image software that can read a stream of bytes and display it as a 4 bytes per pixel image.

Any idea?

Here's what I tried: dump the byte array to a file, push it to /sdcard/img, and load an activity like this:

@Override
public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        final File inputFile = new File("/sdcard/img");
        final FileInputStream isr = new FileInputStream(inputFile);
        final Bitmap bmp = BitmapFactory.decodeStream(isr);
        ImageView iv = new ImageView(this);
        iv.setImageBitmap(bmp);
        setContentView(iv);
        Log.d("ImageTest", "Image was inflated");
    } catch (final FileNotFoundException e) {
        Log.d("ImageTest", "Image was not inflated");
    }
}

I didn't see anything.

Do you know how is encoded the image? Say it is stored into byte[] buffer. buffer[0] is red, buffer[1] is green, etc?


Solution

  • OK -- After quite some unsuccessful tries, I finally got something out of this byte array. I wrote this simple C program to convert the byte array to a Windows Bitmap file. I'm dropping the code in case somebody is interested.
    I compiled this against VisualC 6.0 and gcc 3.4.4, it should work on any OS (tested on Windows, Linux and MacOS X).

    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <stdlib.h>
    
    /* Types */
    typedef unsigned char byte;
    typedef unsigned short uint16_t;
    typedef unsigned int uint32_t;
    typedef int int32_t;
    
    /* Constants */
    #define RMASK 0x00ff0000
    #define GMASK 0x0000ff00
    #define BMASK 0x000000ff
    #define AMASK 0xff000000
    
    /* Structures */
    struct bmpfile_magic {
      unsigned char magic[2];
    };
    
    struct bmpfile_header {
      uint32_t filesz;
      uint16_t creator1;
      uint16_t creator2;
      uint32_t bmp_offset;
    };
    
    struct bmpfile_dibheader {
      uint32_t header_sz;
      uint32_t width;
      uint32_t height;
      uint16_t nplanes;
      uint16_t bitspp;
      uint32_t compress_type;
      uint32_t bmp_bytesz;
      int32_t hres;
      int32_t vres;
      uint32_t ncolors;
      uint32_t nimpcolors;
    
      uint32_t rmask, gmask, bmask, amask;
      uint32_t colorspace_type;
      byte colorspace[0x24];
      uint32_t rgamma, ggamma, bgamma;
    };
    
    /* Displays usage info and exits */
    void usage(char *cmd) {
        printf("Usage:\t%s <img_src> <img_dest.bmp> <width> <height>\n"
            "\timg_src:\timage byte buffer obtained from Eclipse MAT, using 'copy > save value to file' while selecting the byte[] buffer corresponding to an android.graphics.Bitmap\n"
            "\timg_dest:\tpath to target *.bmp file\n"
            "\twidth:\t\tpicture width, obtained in Eclipse MAT, selecting the android.graphics.Bitmap object and seeing the object member values\n"
            "\theight:\t\tpicture height\n\n", cmd);
        exit(1);
    }
    
    /* C entry point */
    int main(int argc, char **argv) {
        FILE *in, *out;
        char *file_in, *file_out;
        int w, h, W, H;
        byte r, g, b, a, *image;
        struct bmpfile_magic magic;
        struct bmpfile_header header;
        struct bmpfile_dibheader dibheader;
    
        /* Parse command line */
        if (argc < 5) {
            usage(argv[0]);
        }
        file_in = argv[1];
        file_out = argv[2];
        W = atoi(argv[3]);
        H = atoi(argv[4]);
        in = fopen(file_in, "rb");
        out = fopen(file_out, "wb");
    
        /* Check parameters */
        if (in == NULL || out == NULL || W == 0 || H == 0) {
            usage(argv[0]);
        }
    
        /* Init BMP headers */
        magic.magic[0] = 'B';
        magic.magic[1] = 'M';
    
        header.filesz = W * H * 4 + sizeof(magic) + sizeof(header) + sizeof(dibheader);
        header.creator1 = 0;
        header.creator2 = 0;
        header.bmp_offset = sizeof(magic) + sizeof(header) + sizeof(dibheader);
    
        dibheader.header_sz = sizeof(dibheader);
        dibheader.width = W;
        dibheader.height = H;
        dibheader.nplanes = 1;
        dibheader.bitspp = 32;
        dibheader.compress_type = 3;
        dibheader.bmp_bytesz = W * H * 4;
        dibheader.hres = 2835;
        dibheader.vres = 2835;
        dibheader.ncolors = 0;
        dibheader.nimpcolors = 0;
        dibheader.rmask = RMASK;
        dibheader.gmask = BMASK;
        dibheader.bmask = GMASK;
        dibheader.amask = AMASK;
        dibheader.colorspace_type = 0x57696e20;
        memset(&dibheader.colorspace, 0, sizeof(dibheader.colorspace));
        dibheader.rgamma = dibheader.bgamma = dibheader.ggamma = 0;
    
        /* Read picture data */
        image = (byte*) malloc(4*W*H);
        if (image == NULL) {
            printf("Could not allocate a %d-byte buffer.\n", 4*W*H);
            exit(1);
        }
        fread(image, 4*W*H, sizeof(byte), in);
        fclose(in);
    
        /* Write header */
        fwrite(&magic, sizeof(magic), 1, out);
        fwrite(&header, sizeof(header), 1, out);
        fwrite(&dibheader, sizeof(dibheader), 1, out);
    
        /* Convert the byte array to BMP format */
        for (h = H-1; h >= 0; h--) {
            for (w = 0; w < W; w++) {
                r = *(image + w*4 + 4 * W * h);
                b = *(image + w*4 + 4 * W * h + 1);
                g = *(image + w*4 + 4 * W * h + 2);
                a = *(image + w*4 + 4 * W * h + 3);
    
                fwrite(&b, 1, 1, out);
                fwrite(&g, 1, 1, out);
                fwrite(&r, 1, 1, out);
                fwrite(&a, 1, 1, out);
            }
        }
    
        free(image);
        fclose(out);
    }
    

    So using this tool I was able to recognise the picture used to generate this 1280x1920 bitmap.