0

I need to create and save single color PNG images (bitmap filled with a single color).

I'm creating the bitmap:

public static Bitmap createColorSwatchBitmap(int width, int height, int color) {
    final Bitmap colorBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    colorBitmap.eraseColor(color);
    return colorBitmap;
}

and saving it to a file on the device storage:

stream = new FileOutputStream(filePath);
success = bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);

If I create a 1200x1200 bitmap, the memory consumption is 5,760,000 bytes (5.76 MB), as reported by bitmap.getAllocationByteCount(). However the PNG file size is only 8,493 bytes.

It seems so overkill to allocate almost 6 MB of memory for a file that will only have 8 KB.

It there a better way?

TechAurelian
  • 5,561
  • 5
  • 50
  • 65

2 Answers2

1

You can use the PNGJ library (disclaimer: I'm the author). Because it saves the image progressively, it only needs to allocate a single row.

For example:

 public static void create(OutputStream os,int cols,int rows,int r,int  g,int  b,int  a)  {     
        ImageInfo imi = new ImageInfo(cols, rows, 8, true); // 8 bits per channel, alpha
        PngWriter png = new PngWriter(os, imi);
        // just a hint to the coder to optimize compression+speed:
        png.setFilterType(FilterType.FILTER_NONE); 
        ImageLineByte iline = new ImageLineByte (imi);
        byte[] scanline = iline.getScanlineByte();// RGBA
        for (int col = 0,pos=0; col < imi.cols; col++) { 
           scanline[pos++]=(byte) r;  
           scanline[pos++]=(byte) g;
           scanline[pos++]=(byte) b;
           scanline[pos++]=(byte) a;
        }
        for (int row = 0; row < png.imgInfo.rows; row++) {
           png.writeRow(iline);
        }
        png.end();   
 }

It seems so overkill to allocate almost 6 MB of memory for a file that will only have 8 KB.

There are two different things here. First, the space wasted in order to allocate the full image in memory - my solution alliviates this, by allocating a single row. But, apart from this, you are making a conceptual error: it make no sense to compare the space allocated in memory with the encoded image size, because PNG is a compressed format (and a single color image will be highly compressed). The memory allocated by any raw editable bitmap (Bitmap in Android, BufferedImage in ImageIO, my own ImageLineByte in PNGJ, or whatever) in practice will never be compressed, and hence it will always waste 4 bytes per pixel - at least. And you can check that: 1200x1200x4=5760000.

leonbloy
  • 73,180
  • 20
  • 142
  • 190
0

You just fill bitmap with only one single color. Why don't you just store colors in SharedPreferences?

It will be much more efficient.

Although, you can just set color background for Views.

Other option is create bitmap of size 1x1 pixel with necessary color and set is as background. It will become size of View.

P.S.

ALPHA_8 don't store color, only alpha. It's completely wrong, check documentation

Community
  • 1
  • 1
Petrov Dmitrii
  • 228
  • 1
  • 10
  • Did you read the question? I need to write and save those bitmaps as PNG files, on the device storage. I don't want to set them as background for views. (Although thank you for the ALPHA_8 tip, I didn't pay enough attention to the documentation.) – TechAurelian Aug 25 '16 at 08:41
  • If your color don't use alpha - then use RGB_565 config. Although, after bitmap is stored - it need be recycle()-d – Petrov Dmitrii Aug 26 '16 at 10:43