×

Discussion Board

Page 1 of 2 12 LastLast
Results 1 to 15 of 16
  1. #1
    Regular Contributor
    Join Date
    Jul 2009
    Posts
    74

    Dealing with large image..

    I'm creating a map application, and, of course it involves using map image as one of its function..
    Problem is, i often get a out of memory error when exploring the map with directional keypad..
    Currently i'm splitting the map into 400 pieces, and i use double buffering with the following code :

    Code:
    package hello;
    
    import java.io.IOException;
    import java.util.Enumeration;
    import java.util.Hashtable;
    import java.util.Vector;
    import javax.microedition.lcdui.Command;
    import javax.microedition.lcdui.CommandListener;
    import javax.microedition.lcdui.Displayable;
    import javax.microedition.lcdui.Graphics;
    import javax.microedition.lcdui.Image;
    import javax.microedition.lcdui.game.GameCanvas;
    
    /**
     *
     * @author yanuar
     */
    public class ImageCanvas extends GameCanvas implements Runnable {
    
        public boolean paintA;
        int move;
        int imgWidth, imgHeight, posX, posY, fImgWidth, fImgHeight, scWidth, scHeight;
        String prefixFile = "/image/Y_";
        Hashtable imgCache;
        Vector imgUsed;
        Peta peta;
        Vector koordinat;
        Graphics g;
        Thread thread;
    
        public ImageCanvas(int imgWidth, int imgHeight, int posX, int posY, int fImgWidth, int fImgHeight, Peta peta) {
            super(true);
            setFullScreenMode(true);
            g = getGraphics();
            move = 50;
            imgCache = new Hashtable();
            imgUsed = new Vector();
            this.peta = peta;
            this.imgWidth = imgWidth;
            this.imgHeight = imgHeight;
            this.posX = posX;
            this.posY = posY;
            this.fImgWidth = fImgWidth;
            this.fImgHeight = fImgHeight;
            this.scWidth = getWidth();
            this.scHeight = getHeight();
            Command back = new Command("Menu Utama", Command.ITEM, 1);
            Command Rute = new Command("Rute", Command.ITEM, 1);
            Command cari = new Command("Find Road", Command.ITEM, 1);
            Listen l = new Listen();
            this.setCommandListener(l);
            addCommand(back);
            addCommand(Rute);
            addCommand(cari);
        }
    
        public void up() {
            if ((posY + move) > 0) {
                return;
            }
            posY += move;
        }
    
        public void down() {
            if ((posY - (2*imgHeight) - move) < -fImgHeight) {
                return;
            }
            posY -= move;
        }
    
        public void left() {
            if ((posX + move) > 0) {
                return;
            }
            posX += move;
        }
    
        public void right() {
            if ((posX - (2*imgWidth) - move) < -fImgWidth) {
                return;
            }
            posX -= move;
        }
    
        protected void draw() {
            g.setColor(255, 0, 0);
            try {
                //long awal = System.currentTimeMillis();
                drawMap();
                //System.out.println("lama "+(System.currentTimeMillis()-awal));
                if (paintA) {
                    drawArah();
                }
                sweepCache();
            } catch (Exception e) {
                peta.getErrorBox().insert(e.toString(), peta.getErrorBox().size());
            }
        }
    
        protected void sweepCache() {
            Enumeration e = imgCache.keys();
            while (e.hasMoreElements()) {
                String key = e.nextElement().toString();
                if (!imgUsed.contains(key)) {
                    imgCache.remove(key);
                }
            }
            imgUsed.removeAllElements();
        }
    
        protected void drawMap() {
            int ib = -(posY / imgHeight);
            //System.out.println(ib);
            for (int j = posY % imgHeight; j < scHeight + imgHeight; j += imgHeight) {
                int ik = -(posX / imgWidth);
                for (int i = posX % imgWidth; i < scWidth; i += imgWidth) {
                    int ki = posX + (imgWidth * ik);
                    int ka = ki + imgWidth;
                    int at = posY + (imgHeight * ib);
                    int bw = at + imgHeight;
                    if (//(0 >= ki && 0 <= ka && 0 >= at && 0 <= bw) ||
                            //  (scWidth >= ki && scWidth <= ka && 0 >= at && 0 <= bw) ||
                            //(0 >= ki && 0 <= ka && scHeight >= at && scHeight <= bw) ||
                            //(scWidth >= ki && scWidth <= ka && scHeight >= at && scHeight <= bw) ||
                            (ki > 0 && ki < scWidth + imgWidth && at > 0 && at < scHeight + imgHeight) ||
                            (ka > 0 && ka < scWidth + imgWidth && at > 0 && at < scHeight + imgHeight) ||
                            (ki > 0 && ki < scWidth + imgWidth && bw > 0 && bw < scHeight + imgHeight) ||
                            (ka > 0 && ka < scWidth + imgWidth && bw > 0 && bw < scHeight + imgHeight)) {
    
                        Image gbr = getImage(prefixFile, ib + 1, ik + 1);
                        g.drawImage(gbr, i, j, 0);
                    }
                    ik++;
                }
                ib++;
            }
        }
    
        protected Image getImage(String prefixFile, int ib, int ik) {
            //System.out.println(ib+","+ik);
            if (ib < 1) {
                ib = 1;
            }
            if (ib > 20) {
                ib = 3;
            }
            if (ik < 1) {
                ik = 1;
            }
            if (ik > 20) {
                ik = 15;
            }
            Image gambar = null;
            try {
                if (imgCache.containsKey(ib + "_" + ik)) {
                    gambar = (Image) imgCache.get(ib + "_" + ik);
                } else {
                    gambar = Image.createImage(prefixFile + ib + "_" + ik + ".png");
                    imgCache.put(ib + "," + ik, gambar);
                }
                imgUsed.addElement(ib + "," + ik);
            } catch (IOException ex) {
                peta.getErrorBox().insert(ex.toString(), peta.getErrorBox().size());
            }
            return gambar;
        }
    
        //int cek;
        public void posisikanPeta() {
            paintA = true;
            this.koordinat = FileResourceReader.koordinat;
            Point p = (Point) koordinat.firstElement();
            posX = -p.x + (scWidth / 2);
            posY = -p.y + (scHeight / 2);
        }
    
        public void posisikanPeta(Point p) {
            koordinat = FileResourceReader.koordinat;
            koordinat.removeAllElements();
            koordinat.addElement(p);
            paintA = true;
            posX = -p.x + (scWidth / 2);
            posY = -p.y + (scHeight / 2);
            g.fillRect(p.x + posX - 3, p.y + posY - 3, 6, 6);
        }
    
        private void drawArah() {
            int halfWScr = (scWidth / 2);
            int halfHScr = (scHeight / 2);
            g.setColor(255, 0, 0);
            Point pasal = (Point) koordinat.firstElement();
            g.fillRect(pasal.x + posX - 3, pasal.y + posY - 3, 6, 6);
            Point pakhir;
            for (int i = 1; i < koordinat.size(); i++) {
                pakhir = (Point) koordinat.elementAt(i);
                g.fillRect(pakhir.x + posX - 3, pakhir.y + posY - 3, 6, 6);
                g.drawLine(pasal.x + posX, pasal.y + posY, pakhir.x + posX, pakhir.y + posY);
                pasal = pakhir;
            }
        }
    
        public void run() {
            while (Thread.currentThread() == thread) {
                int state = getKeyStates();
                //System.out.println(state);
                switch (state) {
                    case UP_PRESSED:
                        ;
                    case KEY_NUM2:
                        up();
                        break;
                    case DOWN_PRESSED:
                        ;
                    case KEY_NUM8:
                        down();
                        break;
                    case LEFT_PRESSED:
                        ;
                    case KEY_NUM4:
                        left();
                        break;
                    case RIGHT_PRESSED:
                        ;
                    case KEY_NUM6:
                        right();
                        break;
    
                }
                try {
                    draw();
                    flushGraphics();
                    Thread.sleep(100);//
                } catch (Exception e) {
                    peta.getErrorBox().insert(e.toString(), peta.getErrorBox().size());
                }
            }
    
        }
    
        protected void hideNotify() {
            thread = null;
        }
    
        protected void showNotify() {
            thread = new Thread(this);
            thread.start();
        }
    
        class Listen implements CommandListener {
    
            public void commandAction(Command c, Displayable d) {
                ImageCanvas cv = (ImageCanvas) d;
                if (c.getLabel().equals("Menu Utama")) {
                    cv.peta.switchDisplayable(null, cv.peta.getMainMenu());
                } else if (c.getLabel().equals("Rute")) {
                    cv.peta.switchDisplayable(null, cv.peta.getHasilPencarian());
                } else if (c.getLabel().equals("Find Road")) {
                    cv.peta.getFilter().modeCari = true;
                    cv.peta.switchDisplayable(null, cv.peta.getFilter());
                }
            }
        }
    }
    So is there any other method that i can use that is more resource-friendly?

    I have another option that i wanted to use before,
    by using smaller map image that contains no road name (just roads image),
    then display the names of the road, according to which frame is shown..

    But the problem with this method is that we cannot rotate the names(text)
    because i use CLDC 1.0 (my target's configuration)
    and AFAIK we can't rotate text without floating point support..
    And that will cause the road names to be jumbled up..

    So does anyone have any other method to deal with such problem?

    thanks.

  2. #2
    Regular Contributor
    Join Date
    Jul 2009
    Posts
    74

    Re: Dealing with large image..

    Almost forgot, the image is 2260x2380 and yes i did optimize it with pngout..
    So can't really optimize it again in terms of size
    I'm looking for the most efficient way to display it without wasting resources.....

    You know google map? I can't figure out how they display it that smoothly..

    thanks.
    Last edited by deny_winarto; 2009-09-07 at 08:02.

  3. #3
    Super Contributor
    Join Date
    Jun 2003
    Location
    Cheshire, UK
    Posts
    7,395

    Re: Dealing with large image..

    Tools like PNGOUT will only help you optimize the size of the PNG file, and so help you with JAR size issues. They don't affect the size of the loaded Image object. The heap size of the Image object is affected only by the number of pixels in the image (width * height).

    What device(s) are you getting an OutOfMemoryError on? Do you know how many images you have loaded at any one time? That is, what is the largest size of imgCache.size()? Remember that this will peak while painting, since you load new images to the cache before discarding unwanted elements.

    (Google Maps on Symbian is a native Symbian app, so they have an advantage over you. Also, they may use tricks like re-using the double-buffer (and having a buffer that is larger than the screen), rather than redrawing it each time, and loading images in a separate thread.)

    Graham.

  4. #4
    Regular Contributor
    Join Date
    Jul 2009
    Posts
    74

    Re: Dealing with large image..

    7610, yeah a ratherd low-end phones...
    But even on the emulator after a few shortest route procedurs;
    it is displaying error, not sure which error though..
    i think it was related to memory, i'll check it again later...
    Is there any other trick to prevent imgCache issue?

    Sorry, i was talking about the exploring speed on my second post,
    it's a different problem with image cache, but i think they are related.

    I meant the jar-google map,
    Is it because it's online? Or is there any other factor on that app that smoothens the exploring process?
    Gmap's exploring function is really smooth even with hundreds of images..

    I can change the "frame speed" by chaning the "move" value in this code, but i can't get similar result :

    Code:
      public ImageCanvas(int imgWidth, int imgHeight, int posX, int posY, int fImgWidth, int fImgHeight, Peta peta) {
            super(true);
            setFullScreenMode(true);
            g = getGraphics();
            move = 50;
            imgCache = new Hashtable();
            imgUsed = new Vector();
            this.peta = peta;
            this.imgWidth = imgWidth;
            this.imgHeight = imgHeight;
            this.posX = posX;
            this.posY = posY;
            this.fImgWidth = fImgWidth;
            this.fImgHeight = fImgHeight;
            this.scWidth = getWidth();
            this.scHeight = getHeight();
            Command back = new Command("Menu Utama", Command.ITEM, 1);
            Command Rute = new Command("Rute", Command.ITEM, 1);
            Command cari = new Command("Find Road", Command.ITEM, 1);
            Listen l = new Listen();
            this.setCommandListener(l);
            addCommand(back);
            addCommand(Rute);
            addCommand(cari);
        }

  5. #5
    Super Contributor
    Join Date
    Jun 2003
    Location
    Cheshire, UK
    Posts
    7,395

    Re: Dealing with large image..

    Quote Originally Posted by deny_winarto View Post
    Is there any other trick to prevent imgCache issue?
    First, you need to establish where your memory is going. How many images do you ever have loaded at one time? You need some debugging to check that, in case your assumptions are wrong.

    Quote Originally Posted by deny_winarto View Post
    I meant the jar-google map,
    Same deal. You spend time re-painting the buffered image each time. When you move a short distance, most of the map is unchanged, it's just painted in a different place. Only the new parts need to be repainted.

    You could consider making the map sections larger.

    However, I'd recommend you get it working first, before you worry about the speed.

    Graham.

  6. #6
    Regular Contributor
    Join Date
    Jul 2009
    Posts
    74

    Re: Dealing with large image..

    First, you need to establish where your memory is going. How many images do you ever have loaded at one time? You need some debugging to check that, in case your assumptions are wrong.
    Okay..
    So once i figure this out, i guess the next step is to adjust the cache to the heap memory of 7610?
    Is there anyway to automatically adjust cache depending on the available heap size?

    There's another i want to ask,
    At first i split the image into 100 pieces, but then I split it into 400 pieces,
    hoping to increase the load times, but the speed seems to get worse...
    So do you think i should decrease or increase the split size?

  7. #7
    Nokia Developer Champion
    Join Date
    Apr 2007
    Posts
    2,708

    Re: Dealing with large image..

    Is it actually the 7610 or the 7610 Supernova ?
    Cause I've seen that even though the 7610 is a S60 it only got 8 megs of User Memory... Which might get filled pretty fast with the sizes of Images you were mentioning...

  8. #8
    Regular Contributor
    Join Date
    Jul 2009
    Posts
    74

    Re: Dealing with large image..

    It's the old 7610...

    Any way to workaround this memory issue?
    I've seen similar map app running fine on this phone...

  9. #9
    Nokia Developer Champion
    Join Date
    Apr 2007
    Posts
    2,708

    Re: Dealing with large image..

    not afaik...
    you might want to have smaller Images or only 1 bigger screen-filling one which gets replaced when a user scrolls...

  10. #10
    Super Contributor
    Join Date
    Jun 2003
    Location
    Cheshire, UK
    Posts
    7,395

    Re: Dealing with large image..

    Bigger map images are likely to make it faster. Smaller images will help reduce the amount of heap used, but will have a performance impact. However, there are ways to improve the performance of scrolling tile maps. I'll describe the technique, but I'll need to remember how to do it first, and then draw some pictures! Anyway, you need to get your current code working first. You're assuming it's the quantity of images you have loaded, but if your code is working, you shouldn't be loading much more than a screen-full of images (so far as I can see). In which case, you shouldn't run out of memory, even on the 7610. Maybe it's loading more images than you think, or maybe the problem is nothing to do with images. Before you try to fix the memory problem, I recommend you pin down exactly what it is.

    Graham.

  11. #11
    Regular Contributor
    Join Date
    Jul 2009
    Posts
    74

    Re: Dealing with large image..

    Ok, thanks again for your help.

    Well it is working, it's just that running the functions several times always causes out of memory error..
    "shortest route" function which involves searching procedure on the database,
    that i told you on the other thread also gives this error after several attempts..

    I'm gonna make a "error box" to show which error is displayed on cellphones..

  12. #12
    Super Contributor
    Join Date
    Jun 2003
    Location
    Cheshire, UK
    Posts
    7,395

    Re: Dealing with large image..

    Sure, it's working fine, apart from the hideous bug that makes it crash! It sounds very much like your route finding algorithm leaks memory, probably by adding objects to a Vector or a Hashtable, and never getting rid of them (eventually running out of memory). If that's the case, then it may be nothing to do with images.

    I understand that you want to improve the performance, but fixing this first will make your life easier. My tips for prioritizing project tasks:

    1. Think about what is required to ship the product. You can ship a working, if slightly slow, product. You can't ship a product that's well optimized but broken.

    2. It's easier to optimize software that works than software that's broken.

    3. Finish a feature completely (and "completely" mean completely - bug free, with no intention to return to it) before you move on to another. This is a basic principle of agile development methods.

    Graham.

  13. #13
    Regular Contributor
    Join Date
    Jul 2009
    Posts
    74

    Re: Dealing with large image..

    Yea you're right i guess..

    But most of my friends that tested this complained about the speed of the map and memory issue..
    that's why i want to fix those things first, and also because that's where i got stuck..

    I'm also correcting database as i fix this problem..
    I made more errors than i thought before

    And apparently the bug is related to Nullpointer..
    It's still throwing Nullpointer exceptions on a few occasions,
    i thought i fixed this already..

  14. #14
    Regular Contributor
    Join Date
    Jul 2009
    Posts
    74

    Re: Dealing with large image..

    So how is it graham, did you remember the technique you said you were going to tell me?

  15. #15
    Super Contributor
    Join Date
    Jun 2003
    Location
    Cheshire, UK
    Posts
    7,395

    Re: Dealing with large image..

    OK... but I don't have any sample code for you, so I hope you have some aspirin. Don't say I didn't warn you.

    The trick is to use a buffer image. The image must be larger than the screen. It must be an exact multiple of the tile size, and larger than the screen by at least one more tile in both directions. So, if the screen is 2.5 tiles wide, the buffer needs to be 4 tiles wide.

    (see the attached image "tiles.png")

    The diagram has three frames.

    In the first frame, you can see the screen (white outline), and the tile-buffer displayed (grey grid - each square on the grid represents one tile). At this point, the tile-buffer is being painted at negative x and y coordinates, so the top-left corner is off the screen. From this point, the user can scroll in any direction, without the need to update the tile-buffer. You can re-use the tile-buffer exactly as it is. No need to load images, no need to draw any tiles on the buffer. Just move the (x,y) where the buffer is being painted.

    Let's say we move "left". To handle this, the tile-buffer is move to the right (the x value increases).

    A problem obviously arises when x = 0, and we reach the left-hand edge of the tile-buffer. We can't continue scrolling, because we have no more tiles. (Frame 2 in the diagram.) We need a new column of tiles.

    Warning: tricky bit!

    Notice in frame 2 that, when the left edge of teh buffer lines up with the screen edge, there is a whole column of tiles on the right edge of the buffer that are now off the screen.

    What we do is: paint the new tiles that are needed for the left-side of the screen, on the right-side of the buffer.

    Next frame (3), we have to paint the tile-buffer twice. First paint is at a positive x position (the grey squares in the diagram). The second paint is at a large negative x position (the green squares - these are the new tiles painted in the right-hand column of the buffer image). Note that the green squares are painted on the left-hand side of the screen, but they're actually on the right-hand side of the buffer.

    This is a fairly simple trick for side-scrolling games that have little or now up and down movement. Once you have four-way scrolling (like you do), it gets more complicated, because you have to handle this process in two dimensions.

    This means that you will end up painted the tile-buffer four times. Each corner of the buffer will contain the tiles for the opposite corner of the screen. So, the tiles needed for the top-left of the screen will be in the bottom-right of the buffer, and so on.

    This technique can massively reduce the number of individual drawImage() calls you need to make for each frame. In MIDP, performance is usually down to the number of calls to drawImage(), not just the number of pixels you paint. That's why smaller tiles make the painting slower. The technique allows you to use smaller tiles (which can help on low memory devices) without the associated drop in performance.

    It works particularly well in games, where the tile map is usually built from a small set of repeated tiles (and all tiles can be loaded at the same time). In your case, each tile is unique, so you will still be loading and unloading tiles as you scroll around.

    You asked about smarter image caching techniques, and there is a trick for this too, but it relies on the WeakReference class which was introduced in CLDC-1.1. Since you want to support CLDC-1.0 devices, you are best to stick with the caching you have (or produce two versions). (Or, do some clever run-time API detection...)

    Before you attempt any of these advanced optimizations, you really want to have the code as solid as possible. Trying to implement such techniques and fix bugs at the same time will be a nightmare.

    Graham.
    Attached Images Attached Images

Similar Threads

  1. diaplay a large image using scroll in J2ME
    By mramin05 in forum Mobile Java Media (Graphics & Sounds)
    Replies: 8
    Last Post: 2009-07-02, 09:19
  2. modifying image on display in canvas
    By aqibarain in forum Mobile Java General
    Replies: 1
    Last Post: 2007-11-30, 09:01
  3. Is MIDP2.0 over Symbian OS slow?
    By epolitakis in forum Mobile Java Games
    Replies: 7
    Last Post: 2007-03-16, 09:32
  4. Opening a JPEG Image
    By ummarbhutta in forum Mobile Java Media (Graphics & Sounds)
    Replies: 8
    Last Post: 2007-02-15, 06:34
  5. how to cut some part of Image
    By mshouab in forum Mobile Java Media (Graphics & Sounds)
    Replies: 2
    Last Post: 2006-08-04, 09:05

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •