altosuilib: Decompress map images asynchronously and in parallel
authorKeith Packard <keithp@keithp.com>
Wed, 28 May 2014 03:34:29 +0000 (20:34 -0700)
committerKeith Packard <keithp@keithp.com>
Wed, 28 May 2014 03:34:29 +0000 (20:34 -0700)
This speeds up loading map images from disk quite a bit, and keeps the
UI responsive while that happens as well.

Signed-off-by: Keith Packard <keithp@keithp.com>
altosuilib/AltosSiteMap.java
altosuilib/AltosSiteMapCache.java
altosuilib/AltosSiteMapImage.java [new file with mode: 0644]
altosuilib/AltosSiteMapTile.java
altosuilib/Makefile.am

index f22d9531848acd4d87be60ae4b5ed06946e7d155..ee9b8c051658cdfdae26743200c4f2ccc4ba97b0 100644 (file)
@@ -50,7 +50,7 @@ class MapPoint {
        }
 }
 
        }
 }
 
-public class AltosSiteMap extends JComponent implements AltosFlightDisplay, MouseMotionListener, MouseListener {
+public class AltosSiteMap extends JComponent implements AltosFlightDisplay, MouseMotionListener, MouseListener, HierarchyBoundsListener {
        // preferred vertical step in a tile in naut. miles
        // will actually choose a step size between x and 2x, where this
        // is 1.5x
        // preferred vertical step in a tile in naut. miles
        // will actually choose a step size between x and 2x, where this
        // is 1.5x
@@ -213,11 +213,15 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous
                tile.set_status(AltosSiteMapCache.loading);
                int status = AltosSiteMapCache.fetch_map(pngfile, pngurl);
                if (status == AltosSiteMapCache.success) {
                tile.set_status(AltosSiteMapCache.loading);
                int status = AltosSiteMapCache.fetch_map(pngfile, pngurl);
                if (status == AltosSiteMapCache.success) {
-                       SwingUtilities.invokeLater(new Runnable() {
-                                       public void run() {
-                                               tile.load_map(pngfile);
-                                       }
-                               });
+                       if (SwingUtilities.isEventDispatchThread())
+                               tile.load_map(pngfile);
+                       else {
+                               SwingUtilities.invokeLater(new Runnable() {
+                                               public void run() {
+                                                       tile.load_map(pngfile);
+                                               }
+                                       });
+                       }
                } else {
                        tile.set_status(status);
                        System.out.printf("# Failed to fetch file %s (status %d)\n", pngfile, status);
                } else {
                        tile.set_status(status);
                        System.out.printf("# Failed to fetch file %s (status %d)\n", pngfile, status);
@@ -298,7 +302,11 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous
                Thread thread = new Thread() {
                                public void run() {
                                        init_map(offset, load_mode_cached|load_mode_uncached);
                Thread thread = new Thread() {
                                public void run() {
                                        init_map(offset, load_mode_cached|load_mode_uncached);
-                                       finishTileLater(tile, offset);
+                                       SwingUtilities.invokeLater( new Runnable() {
+                                                       public void run() {
+                                                               addTileAt(tile, offset);
+                                                       }
+                                               } );
                                }
                        };
                thread.start();
                                }
                        };
                thread.start();
@@ -315,8 +323,6 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous
        }
 
        public void setBaseLocation(double lat, double lng) {
        }
 
        public void setBaseLocation(double lat, double lng) {
-               for (AltosSiteMapTile tile : mapTiles.values())
-                       tile.clearMap();
                this.lat = lat;
                this.lon = lng;
                base_location_set = true;
                this.lat = lat;
                this.lon = lng;
                base_location_set = true;
@@ -328,6 +334,8 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous
        private void initMaps(double lat, double lng) {
                setBaseLocation(lat, lng);
 
        private void initMaps(double lat, double lng) {
                setBaseLocation(lat, lng);
 
+               for (AltosSiteMapTile tile : mapTiles.values())
+                       tile.clearMap();
                Thread thread = new Thread() {
                                public void run() {
                                        for (Point k : mapTiles.keySet())
                Thread thread = new Thread() {
                                public void run() {
                                        for (Point k : mapTiles.keySet())
@@ -379,7 +387,7 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous
        JLabel  zoom_label;
 
        public void set_zoom_label() {
        JLabel  zoom_label;
 
        public void set_zoom_label() {
-               zoom_label.setText(String.format("- %d -", zoom - default_zoom));
+               zoom_label.setText(String.format("Zoom %d", zoom - default_zoom));
        }
 
        public void set_zoom(int zoom) {
        }
 
        public void set_zoom(int zoom) {
@@ -524,15 +532,6 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous
                mapTiles.put(offset, tile);
                return tile;
        }
                mapTiles.put(offset, tile);
                return tile;
        }
-       private void finishTileLater(final AltosSiteMapTile tile,
-                                    final Point offset)
-       {
-               SwingUtilities.invokeLater( new Runnable() {
-                       public void run() {
-                               addTileAt(tile, offset);
-                       }
-               } );
-       }
 
        private void ensureTilesAround(Point base_offset) {
                for (int x = -radius; x <= radius; x++) {
 
        private void ensureTilesAround(Point base_offset) {
                for (int x = -radius; x <= radius; x++) {
@@ -629,6 +628,14 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous
                }
        }
 
                }
        }
 
+       static void debug_component(Component who, String where) {
+//             Rectangle       r = who.getBounds();
+//             int             x = r.x / px_size;
+//             int             y = r.y / px_size;
+//
+//             System.out.printf ("%3d, %3d: %s\n", x, y, where);
+       }
+
        LatLng latlng(MouseEvent e) {
                if (!base_location_set)
                        return null;
        LatLng latlng(MouseEvent e) {
                if (!base_location_set)
                        return null;
@@ -672,6 +679,24 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous
        public void mouseReleased(MouseEvent e) {
        }
 
        public void mouseReleased(MouseEvent e) {
        }
 
+       public void set_cache_size() {
+               Rectangle       r = comp.getVisibleRect();
+
+               int     width_tiles = (r.width + 2*px_size) / px_size;
+               int     height_tiles = (r.height + 2*px_size) / px_size;
+               int     tiles = width_tiles * height_tiles;
+               AltosSiteMapCache.set_cache_size(tiles);
+       }
+
+       /* HierarchyBoundsListener methods */
+       public void ancestorMoved(HierarchyEvent e) {
+               set_cache_size();
+       }
+
+       public void ancestorResized(HierarchyEvent e) {
+               set_cache_size();
+       }
+
        JScrollPane     pane = new JScrollPane();
 
        public AltosSiteMap(int in_radius) {
        JScrollPane     pane = new JScrollPane();
 
        public AltosSiteMap(int in_radius) {
@@ -681,6 +706,7 @@ public class AltosSiteMap extends JComponent implements AltosFlightDisplay, Mous
 
                comp.addMouseMotionListener(this);
                comp.addMouseListener(this);
 
                comp.addMouseMotionListener(this);
                comp.addMouseListener(this);
+               comp.addHierarchyBoundsListener(this);
 
                GrabNDrag scroller = new GrabNDrag(comp);
 
 
                GrabNDrag scroller = new GrabNDrag(comp);
 
index 42914debf78412d6cb8a8d4ef811450e7a944d9b..6e6046bc7f3f31d071f956f48eb5ac1298a1e7b0 100644 (file)
@@ -24,62 +24,6 @@ import java.awt.*;
 import java.io.*;
 import java.net.*;
 
 import java.io.*;
 import java.net.*;
 
-class AltosCacheImage {
-       Component       component;
-       File            file;
-       VolatileImage   image;
-       int             width;
-       int             height;
-       long            used;
-
-       public void load_image() throws IOException {
-               BufferedImage   bimg = ImageIO.read(file);
-               if (bimg == null)
-                       throw new IOException("Can't load image file");
-               Graphics2D      g = image.createGraphics();
-               g.drawImage(bimg, 0, 0, null);
-               bimg.flush();
-               bimg = null;
-       }
-
-       public Image validate() {
-               int     returnCode;
-
-               if (image != null)
-                       returnCode = image.validate(component.getGraphicsConfiguration());
-               else
-                       returnCode = VolatileImage.IMAGE_INCOMPATIBLE;
-               if (returnCode == VolatileImage.IMAGE_RESTORED) {
-                       try {
-                               load_image();
-                       } catch (IOException e) {
-                               return null;
-                       }
-               } else if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE) {
-                       image = component.createVolatileImage(width, height);
-                       try {
-                               load_image();
-                       } catch (IOException e) {
-                               return null;
-                       }
-               }
-               return image;
-       }
-
-       public void flush() {
-               image.flush();
-       }
-
-       public AltosCacheImage(Component component, File file, int w, int h) throws IOException {
-               this.component = component;
-               this.file = file;
-               width = w;
-               height = h;
-               image = component.createVolatileImage(w, h);
-               used = 0;
-       }
-}
-
 public class AltosSiteMapCache {
        static final long google_maps_ratelimit_ms = 1200;
        // Google limits static map queries to 50 per minute per IP, so
 public class AltosSiteMapCache {
        static final long google_maps_ratelimit_ms = 1200;
        // Google limits static map queries to 50 per minute per IP, so
@@ -99,121 +43,197 @@ public class AltosSiteMapCache {
        static boolean  forbidden_set = false;
        static final long       forbidden_interval = 60l * 1000l * 1000l * 1000l;
 
        static boolean  forbidden_set = false;
        static final long       forbidden_interval = 60l * 1000l * 1000l * 1000l;
 
-       public static synchronized int fetch_map(File file, String url) {
+       static private Object fetch_lock = new Object();
+
+       public static int fetch_map(File file, String url) {
                if (file.exists())
                        return success;
 
                if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval)
                        return forbidden;
 
                if (file.exists())
                        return success;
 
                if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval)
                        return forbidden;
 
-               URL u;
-               long startTime = System.nanoTime();
+               synchronized (fetch_lock) {
+                       URL u;
+                       long startTime = System.nanoTime();
 
 
-               try {
-                       u = new URL(url);
-               } catch (java.net.MalformedURLException e) {
-                       return bad_request;
-               }
+                       try {
+                               u = new URL(url);
+                       } catch (java.net.MalformedURLException e) {
+                               return bad_request;
+                       }
 
 
-               byte[] data;
-               URLConnection uc = null;
-               try {
-                       uc = u.openConnection();
-                       String type = uc.getContentType();
-                       int contentLength = uc.getContentLength();
-                       if (uc instanceof HttpURLConnection) {
-                               int response = ((HttpURLConnection) uc).getResponseCode();
-                               switch (response) {
-                               case HttpURLConnection.HTTP_FORBIDDEN:
-                               case HttpURLConnection.HTTP_PAYMENT_REQUIRED:
-                               case HttpURLConnection.HTTP_UNAUTHORIZED:
-                                       forbidden_time = System.nanoTime();
-                                       forbidden_set = true;
-                                       return forbidden;
+                       byte[] data;
+                       URLConnection uc = null;
+                       try {
+                               uc = u.openConnection();
+                               String type = uc.getContentType();
+                               int contentLength = uc.getContentLength();
+                               if (uc instanceof HttpURLConnection) {
+                                       int response = ((HttpURLConnection) uc).getResponseCode();
+                                       switch (response) {
+                                       case HttpURLConnection.HTTP_FORBIDDEN:
+                                       case HttpURLConnection.HTTP_PAYMENT_REQUIRED:
+                                       case HttpURLConnection.HTTP_UNAUTHORIZED:
+                                               forbidden_time = System.nanoTime();
+                                               forbidden_set = true;
+                                               return forbidden;
+                                       }
                                }
                                }
-                       }
-                       InputStream in = new BufferedInputStream(uc.getInputStream());
-                       int bytesRead = 0;
-                       int offset = 0;
-                       data = new byte[contentLength];
-                       while (offset < contentLength) {
-                               bytesRead = in.read(data, offset, data.length - offset);
-                               if (bytesRead == -1)
-                                       break;
-                               offset += bytesRead;
-                       }
-                       in.close();
+                               InputStream in = new BufferedInputStream(uc.getInputStream());
+                               int bytesRead = 0;
+                               int offset = 0;
+                               data = new byte[contentLength];
+                               while (offset < contentLength) {
+                                       bytesRead = in.read(data, offset, data.length - offset);
+                                       if (bytesRead == -1)
+                                               break;
+                                       offset += bytesRead;
+                               }
+                               in.close();
 
 
-                       if (offset != contentLength)
+                               if (offset != contentLength)
+                                       return failed;
+
+                       } catch (IOException e) {
                                return failed;
                                return failed;
+                       }
 
 
-               } catch (IOException e) {
-                       return failed;
-               }
+                       try {
+                               FileOutputStream out = new FileOutputStream(file);
+                               out.write(data);
+                               out.flush();
+                               out.close();
+                       } catch (FileNotFoundException e) {
+                               return bad_request;
+                       } catch (IOException e) {
+                               if (file.exists())
+                                       file.delete();
+                               return bad_request;
+                       }
 
 
-               try {
-                       FileOutputStream out = new FileOutputStream(file);
-                       out.write(data);
-                       out.flush();
-                       out.close();
-               } catch (FileNotFoundException e) {
-                       return bad_request;
-               } catch (IOException e) {
-                       if (file.exists())
-                               file.delete();
-                       return bad_request;
+                       long duration_ms = (System.nanoTime() - startTime) / 1000000;
+                       if (duration_ms < google_maps_ratelimit_ms) {
+                               try {
+                                       Thread.sleep(google_maps_ratelimit_ms - duration_ms);
+                               } catch (InterruptedException e) {
+                                       Thread.currentThread().interrupt();
+                               }
+                       }
+                       return success;
                }
                }
+       }
 
 
-               long duration_ms = (System.nanoTime() - startTime) / 1000000;
-               if (duration_ms < google_maps_ratelimit_ms) {
-                       try {
-                               Thread.sleep(google_maps_ratelimit_ms - duration_ms);
-                       } catch (InterruptedException e) {
-                               Thread.currentThread().interrupt();
+       static final int                min_cache_size = 9;
+       static final int                max_cache_size = 24;
+
+       static int                      cache_size = min_cache_size;
+
+       static AltosSiteMapImage[]      images = new AltosSiteMapImage[cache_size];
+
+       static Object cache_lock = new Object();
+
+       public  static void set_cache_size(int new_size) {
+               if (new_size < min_cache_size)
+                       new_size = min_cache_size;
+               if (new_size > max_cache_size)
+                       new_size = max_cache_size;
+               if (new_size == cache_size)
+                       return;
+
+               synchronized(cache_lock) {
+                       AltosSiteMapImage[]     new_images = new AltosSiteMapImage[new_size];
+
+                       for (int i = 0; i < cache_size; i++) {
+                               if (i < new_size)
+                                       new_images[i] = images[i];
+                               else
+                                       images[i].flush();
                        }
                        }
+                       images = new_images;
+                       cache_size = new_size;
                }
                }
+       }
 
 
-               return success;
+       static long                     used;
+
+       private static Point tile_loc(AltosSiteMapTile tile) {
+               Rectangle       r = tile.getBounds();
+               int             x = r.x / 512;
+               int             y = r.y / 512;
+
+               return new Point (x, y);
        }
 
        }
 
-       static final int                cache_size = 12;
+       private static void dump_cache() {
+               int     min_x = 1000, max_x = -1000, min_y = 1000, max_y = -1000;
 
 
-       static AltosCacheImage[]        images;
+               for (int i = 0; i < cache_size; i++) {
+                       AltosSiteMapImage       image = images[i];
+                       if (image != null) {
+                               Point p = tile_loc(image.tile);
+                               min_x = min_x < p.x ? min_x : p.x;
+                               max_x = max_x > p.x ? max_x : p.x;
+                               min_y = min_y < p.y ? min_y : p.y;
+                               max_y = max_y > p.y ? max_y : p.y;
+                               System.out.printf ("entry %d %d,%d used %d\n", i, p.x, p.y, image.used);
+                       } else {
+                               System.out.printf ("entry %d empty\n", i);
+                       }
+               }
 
 
-       static long                     used;
+               int[][] map = new int[max_x - min_x + 1][max_y - min_y + 1];
+               for (int i = 0; i < cache_size; i++) {
+                       AltosSiteMapImage       image = images[i];
+                       if (image != null) {
+                               Point p = tile_loc(image.tile);
+                               map[p.x - min_x][p.y - min_y]++;
+                       }
+               }
 
 
-       public static Image get_image(Component component, File file, int width, int height) {
+               for (int y = min_y; y <= max_y; y++) {
+                       for (int x = min_x; x <= max_x; x++)
+                               System.out.printf (" %2d", map[x - min_x][y - min_y]);
+                       System.out.printf("\n");
+               }
+       }
+
+       public static AltosSiteMapImage get_image(AltosSiteMapTile tile, File file, int width, int height) {
                int             oldest = -1;
                long            age = used;
                int             oldest = -1;
                long            age = used;
-               AltosCacheImage image;
-               if (images == null)
-                       images = new AltosCacheImage[cache_size];
-               for (int i = 0; i < cache_size; i++) {
-                       image = images[i];
 
 
-                       if (image == null) {
-                               oldest = i;
-                               break;
+               synchronized(cache_lock) {
+                       AltosSiteMapImage       image = null;
+                       for (int i = 0; i < cache_size; i++) {
+                               image = images[i];
+
+                               if (image == null) {
+                                       oldest = i;
+                                       break;
+                               }
+                               if (image.tile == tile && file.equals(image.file)) {
+                                       image.used = used++;
+                                       return image;
+                               }
+                               if (image.used < age) {
+                                       oldest = i;
+                                       age = image.used;
+                               }
                        }
                        }
-                       if (image.component == component && file.equals(image.file)) {
+
+                       try {
+                               image = new AltosSiteMapImage(tile, file, width, height);
                                image.used = used++;
                                image.used = used++;
-                               return image.validate();
-                       }
-                       if (image.used < age) {
-                               oldest = i;
-                               age = image.used;
+                               if (images[oldest] != null) {
+//                                     dump_cache();
+                                       AltosSiteMap.debug_component(images[oldest].tile, "replacing cache");
+                                       AltosSiteMap.debug_component(tile, "replaced cache");
+                                       images[oldest].flush();
+                               }
+                               images[oldest] = image;
+                               return image;
+                       } catch (IOException e) {
+                               return null;
                        }
                }
                        }
                }
-
-               try {
-                       image = new AltosCacheImage(component, file, width, height);
-                       image.used = used++;
-                       if (images[oldest] != null)
-                               images[oldest].flush();
-                       images[oldest] = image;
-                       return image.validate();
-               } catch (IOException e) {
-                       return null;
-               }
        }
 }
        }
 }
diff --git a/altosuilib/AltosSiteMapImage.java b/altosuilib/AltosSiteMapImage.java
new file mode 100644 (file)
index 0000000..f08c0b2
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.image.*;
+import javax.imageio.ImageIO;
+import javax.swing.*;
+import java.io.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosSiteMapImage {
+       AltosSiteMapTile        tile;
+       File                    file;
+       BufferedImage           image;
+       int                     width;
+       int                     height;
+       long                    used;
+
+       Thread  load_thread;
+
+       public boolean validate() {
+               if (image != null) {
+                       AltosSiteMap.debug_component(tile, "valid");
+                       return true;
+               } else {
+                       AltosSiteMap.debug_component(tile, "loading");
+                       load_thread = new Thread() {
+                                       public void run() {
+                                               image = null;
+                                               try {
+                                                       image = ImageIO.read(file);
+                                               } catch (Exception e) {
+                                               }
+                                               SwingUtilities.invokeLater( new Runnable() {
+                                                               public void run() {
+                                                                       AltosSiteMap.debug_component(tile, "later");
+                                                                       Graphics2D g2d = (Graphics2D) tile.getGraphics();
+                                                                       tile.paint_graphics(g2d, image);
+                                                                       load_thread = null;
+                                                               }
+                                                       });
+                                       }
+                               };
+                       load_thread.start();
+                       return false;
+               }
+       }
+
+       public void flush() {
+               if (load_thread == null) {
+                       AltosSiteMap.debug_component(tile, "flush");
+                       image.flush();
+                       image = null;
+               }
+       }
+
+       public AltosSiteMapImage (AltosSiteMapTile tile, File file, int w, int h) throws IOException {
+               this.tile = tile;
+               this.file = file;
+               width = w;
+               height = h;
+               image = null;
+               used = 0;
+       }
+}
+
index 09f184a37869cf121f19e3f556b535f10ca7e4ea..136fbd7a23095b00c7b7c2283fd6516e36e0077f 100644 (file)
@@ -51,11 +51,15 @@ public class AltosSiteMapTile extends JComponent {
        LinkedList<AltosPoint>  points;
 
        public synchronized void queue_repaint() {
        LinkedList<AltosPoint>  points;
 
        public synchronized void queue_repaint() {
-               SwingUtilities.invokeLater(new Runnable() {
-                               public void run() {
-                                       repaint();
-                               }
-                       });
+               if (SwingUtilities.isEventDispatchThread())
+                       repaint();
+               else {
+                       SwingUtilities.invokeLater(new Runnable() {
+                                       public void run() {
+                                               repaint();
+                                       }
+                               });
+               }
        }
 
        public void load_map(File pngFile) {
        }
 
        public void load_map(File pngFile) {
@@ -68,13 +72,14 @@ public class AltosSiteMapTile extends JComponent {
        public void set_font(Font font) {
                this.font = font;
                this.status = AltosSiteMapCache.success;
        public void set_font(Font font) {
                this.font = font;
                this.status = AltosSiteMapCache.success;
-               queue_repaint();
        }
 
        public void set_status(int status) {
        }
 
        public void set_status(int status) {
-               file = null;
-               this.status = status;
-               queue_repaint();
+               if (status != this.status || file != null) {
+                       file = null;
+                       this.status = status;
+                       queue_repaint();
+               }
        }
 
        public void clearMap() {
        }
 
        public void clearMap() {
@@ -83,7 +88,6 @@ public class AltosSiteMapTile extends JComponent {
                points = null;
                file = null;
                status = AltosSiteMapCache.success;
                points = null;
                file = null;
                status = AltosSiteMapCache.success;
-               queue_repaint();
                line = null;
        }
 
                line = null;
        }
 
@@ -151,17 +155,14 @@ public class AltosSiteMapTile extends JComponent {
                return String.format(format, distance);
        }
 
                return String.format(format, distance);
        }
 
-       public void paint(Graphics g) {
-               Graphics2D      g2d = (Graphics2D) g;
-               AltosPoint      prev = null;
-               Image           img = null;
+       boolean painting;
 
 
-               if (file != null)
-                       img = AltosSiteMapCache.get_image(this, file, px_size, px_size);
-
-               if (img != null) {
-                       g2d.drawImage(img, 0, 0, null);
+       public void paint_graphics(Graphics2D g2d, Image image) {
+               if (image != null) {
+                       AltosSiteMap.debug_component(this, "paint_graphics");
+                       g2d.drawImage(image, 0, 0, null);
                } else {
                } else {
+                       AltosSiteMap.debug_component(this, "erase_graphics");
                        g2d.setColor(Color.GRAY);
                        g2d.fillRect(0, 0, getWidth(), getHeight());
                        String  message = null;
                        g2d.setColor(Color.GRAY);
                        g2d.fillRect(0, 0, getWidth(), getHeight());
                        String  message = null;
@@ -199,6 +200,7 @@ public class AltosSiteMapTile extends JComponent {
                g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
 
                if (points != null) {
                g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
 
                if (points != null) {
+                       AltosPoint              prev = null;
                        for (AltosPoint point : points) {
                                if (prev != null) {
                                        if (0 <= point.state && point.state < stateColors.length)
                        for (AltosPoint point : points) {
                                if (prev != null) {
                                        if (0 <= point.state && point.state < stateColors.length)
@@ -237,9 +239,37 @@ public class AltosSiteMapTile extends JComponent {
                        }
                        g2d.drawString(message, x, y);
                }
                        }
                        g2d.drawString(message, x, y);
                }
+               painting = false;
+       }
+
+       public void paint(Graphics g) {
+               Graphics2D              g2d = (Graphics2D) g;
+               Image                   image = null;
+               boolean                 queued = false;
+
+               if (painting) {
+                       AltosSiteMap.debug_component(this, "already painting");
+                       return;
+               }
+               AltosSiteMap.debug_component(this, "paint");
+
+               if (file != null) {
+                       AltosSiteMapImage       aimage;
+
+                       aimage = AltosSiteMapCache.get_image(this, file, px_size, px_size);
+                       if (aimage != null) {
+                               if (aimage.validate())
+                                       image = aimage.image;
+                               else
+                                       queued = true;
+                       }
+               }
+               if (!queued)
+                       paint_graphics(g2d, image);
+               painting = queued;
        }
 
        }
 
-       public synchronized void show(int state, Point2D.Double last_pt, Point2D.Double pt)
+       public void show(int state, Point2D.Double last_pt, Point2D.Double pt)
        {
                if (points == null)
                        points = new LinkedList<AltosPoint>();
        {
                if (points == null)
                        points = new LinkedList<AltosPoint>();
index b7d624e2e4843b0c4778b0b683bbeb3fb498911a..10b756b8873aa82275e69b4f99861c972781f0a7 100644 (file)
@@ -37,6 +37,7 @@ altosuilib_JAVA = \
        AltosSiteMapCache.java \
        AltosSiteMapPreload.java \
        AltosSiteMapTile.java \
        AltosSiteMapCache.java \
        AltosSiteMapPreload.java \
        AltosSiteMapTile.java \
+       AltosSiteMapImage.java \
        AltosVoice.java \
        AltosDisplayThread.java \
        AltosFreqList.java
        AltosVoice.java \
        AltosDisplayThread.java \
        AltosFreqList.java