0

I have this working code which reads in a 700x700 RGB24 TIF file and places it into display memory. The line which assigns the pixelARGB value appears to be extremely inefficient, this code takes 3-4 seconds to redraw the screen. Is there a way I can avoid the shifting and oring and just place the byte values into the correct position within the 32 bit word? In other languages I have done this with "overlayed variables" or "variant records" or such. Cannot find this in Java. Thank you.

for (y=0; y<700; y++) { // for each line
    i = 0;
    for (x=0; x<700; x++) { // for each dot
        red = lineBuf[i++] & 0xFF;
        green = lineBuf[i++] & 0xFF;
        blue = lineBuf[i++]& 0xFF;
        pixelARGB = 0xFF000000 | (red << 16)| (green << 8) | blue;
        this_g.setPixel(x + BORDER, y + BORDER, pixelARGB);
    }
    size=is.read(lineBuf,0,2100);
}
user1644002
  • 3,211
  • 3
  • 19
  • 21
  • Have you tried this? int ARGB = new Color(red, green, blue, alpha).getRGB(); – Pragnani Mar 27 '13 at 19:33
  • what type of array is `lineBuf`, if it's an `int` array then a lot of memory / time is being wasted, if not then all the `& 0xFF` masks are being wasted. – Serdalis Mar 27 '13 at 19:33
  • Serdalis: LineBuf is a byte array, you are right the & 0xFF appear wasted. I will remove it and retest.Thanks. Pragnini: Thanks I will try it. – user1644002 Mar 27 '13 at 20:11
  • Serdalis: If I remove the masking, then the final pixels displayed are corrupted; apparently there is sign extension occurring and when the top bit is set the bits above all go to "1" causing a white display. – user1644002 Mar 27 '13 at 22:04
  • Pragnani: I was able to replace the rotates and shifts with: pixelARGB=Color.argb(alpha,red,green,blue); which is similar to your suggestion; however, performance stayed about the same. It must be slow due to the numerous setPixel calls as David (below) suggested. Thank you for your help. – user1644002 Mar 27 '13 at 22:57

1 Answers1

1

There is at least one way to convert your TIFF image data buffer into a Bitmap more efficiently, and there is an optimization that can possibly be made.

1. Use an int[] array instead of pixel copies:

You still have to calculate each pixel individually, but set them in an int[] array. It is the setPixel() function that is taking all your time.

Example:

final int w = 700;
final int h = 700;
final int n = w * h;
final int [] buf = new int[n];
for (int y = 0; y < h; y++) {
    final int yw = y * w;
    for (int x = 0; x < w; x++) {
        int i = yw + x;
        // Calculate 'pixelARGB' here.
        buf[i] = pixelARGB;
    }
}
Bitmap result = Bitmap.createBitmap(buf, w, h, Bitmap.Config.ARGB_8888);

2. Resize within your Loop:

This is not very likely, but in case your destination ImageView for the result image is known to be smaller than the source image, which is 700x700 in your question, then you can resize within your for loop for an extremely high performance increase.

What you have to do is loop through your destination image pixels, calculate the pixel x, y values you need from your source image, calculate the pixelARGB value for only those pixels, populate a smaller int[] array, and finally generate a smaller Bitmap. Much. Faster.

You can even enhance the resize quality with a homebrew cubic interpolation of the four nearest source pixels for each destination pixel, but I think you will find this unnecessary for display purposes.

David Manpearl
  • 12,362
  • 8
  • 55
  • 72
  • David, This 700x700 image is being drawn into the middle of an existing 800x800 bitmap; so I have to see if I can insert the 700x700 area into the middle-- I would believe you that the setPixel is burning up much of the time. I couldn't find a set Row function or I would have used that. Regarding #2 the file has the same resolution as the drawn map so there is no downsizeing to be made. Thanks – user1644002 Mar 27 '13 at 20:24
  • You're welcome. I didn't expect #2 to be able to help, but one must try because the improvement can be so great. Regarding your source 700x700 vs destination 800x800, I recommend that you allow Android to scale that at display time via standard `Layout` parameters. It is very efficient. Note that the size will not always display exactly as you expect because the Android OS further scales the image based on screen density so that all images will appear the same physical size on all devices - this is often a confusing pain for developers. – David Manpearl Mar 27 '13 at 20:50
  • Taking David's advice, I have minimized the system calls down to two, one to read in the rgb elements of the TIFF and one to output a bitmap onto a preexisting canvas. Hoping compiler treats those << 8's intelligently (as a byte move rather than bit manipulation). byte[] lineBuf=new byte[2100*700]; int [] bmapBuf=new int[490000];bmapBuf[j++]=((((0x0000FF00 | (lineBuf[i++] & 0xff))<<8)| (lineBuf[i++] & 0xff ))<<8) | (lineBuf[i++] & 0xff ); this_g.drawBitmap(bmapBuf,0,700,BORDER,BORDER,700,700,true,null); – user1644002 Mar 29 '13 at 16:07