Ignore:
Timestamp:
2018-09-30T20:11:41+02:00 (6 years ago)
Author:
Don-vip
Message:

fix #16778 - cache padded icons to improve performance when filtering relations

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/tools/ImageProvider.java

    r14238 r14284  
    3434import java.util.Base64;
    3535import java.util.Collection;
    36 import java.util.Collections;
    3736import java.util.HashMap;
    3837import java.util.HashSet;
     
    271270    }
    272271
    273     /** small cache of critical images used in many parts of the application */
    274     private static final Map<OsmPrimitiveType, ImageIcon> osmPrimitiveTypeCache = Collections.synchronizedMap(new HashMap<>());
    275 
    276272    /** directories in which images are searched */
    277273    protected Collection<String> dirs;
     
    317313    private static final Map<Image, Map<Long, Image>> ROTATE_CACHE = new HashMap<>();
    318314
     315    /** small cache of critical images used in many parts of the application */
     316    private static final Map<OsmPrimitiveType, ImageIcon> osmPrimitiveTypeCache = new HashMap<>();
     317
     318    /** larger cache of critical padded image icons used in many parts of the application */
     319    private static final Map<Dimension, Map<MapImage, ImageIcon>> paddedImageCache = new HashMap<>();
     320
    319321    private static final ExecutorService IMAGE_FETCHER =
    320322            Executors.newSingleThreadExecutor(Utils.newThreadFactory("image-fetcher-%d", Thread.NORM_PRIORITY));
     
    834836
    835837    /**
    836      * Clears the internal image cache.
     838     * Clears the internal image caches.
    837839     * @since 11021
    838840     */
     
    840842        synchronized (cache) {
    841843            cache.clear();
     844        }
     845        synchronized (ROTATE_CACHE) {
     846            ROTATE_CACHE.clear();
     847        }
     848        synchronized (paddedImageCache) {
     849            paddedImageCache.clear();
     850        }
     851        synchronized (osmPrimitiveTypeCache) {
     852            osmPrimitiveTypeCache.clear();
    842853        }
    843854    }
     
    14781489    public static ImageIcon get(OsmPrimitiveType type) {
    14791490        CheckParameterUtil.ensureParameterNotNull(type, "type");
    1480         return osmPrimitiveTypeCache.computeIfAbsent(type, t -> get("data", t.getAPIName()));
     1491        synchronized (osmPrimitiveTypeCache) {
     1492            return osmPrimitiveTypeCache.computeIfAbsent(type, t -> get("data", t.getAPIName()));
     1493        }
    14811494    }
    14821495
     
    14901503        // Check if the current styles have special icon for tagged objects.
    14911504        if (primitive.isTagged()) {
    1492             Pair<StyleElementList, Range> nodeStyles;
    1493             DataSet ds = primitive.getDataSet();
    1494             if (ds != null) {
    1495                 ds.getReadLock().lock();
    1496             }
    1497             try {
    1498                 nodeStyles = MapPaintStyles.getStyles().generateStyles(primitive, 100, false);
    1499             } finally {
    1500                 if (ds != null) {
    1501                     ds.getReadLock().unlock();
    1502                 }
    1503             }
    1504             for (StyleElement style : nodeStyles.a) {
    1505                 if (style instanceof NodeElement) {
    1506                     NodeElement nodeStyle = (NodeElement) style;
    1507                     MapImage icon = nodeStyle.mapImage;
    1508                     if (icon != null) {
    1509                         int backgroundRealWidth = GuiSizesHelper.getSizeDpiAdjusted(iconSize.width);
    1510                         int backgroundRealHeight = GuiSizesHelper.getSizeDpiAdjusted(iconSize.height);
    1511                         int iconRealWidth = icon.getWidth();
    1512                         int iconRealHeight = icon.getHeight();
    1513                         BufferedImage image = new BufferedImage(backgroundRealWidth, backgroundRealHeight,
    1514                                 BufferedImage.TYPE_INT_ARGB);
    1515                         double scaleFactor = Math.min(backgroundRealWidth / (double) iconRealWidth, backgroundRealHeight
    1516                                 / (double) iconRealHeight);
    1517                         Image iconImage = icon.getImage(false);
    1518                         Image scaledIcon;
    1519                         final int scaledWidth;
    1520                         final int scaledHeight;
    1521                         if (scaleFactor < 1) {
    1522                             // Scale icon such that it fits on background.
    1523                             scaledWidth = (int) (iconRealWidth * scaleFactor);
    1524                             scaledHeight = (int) (iconRealHeight * scaleFactor);
    1525                             scaledIcon = iconImage.getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
    1526                         } else {
    1527                             // Use original size, don't upscale.
    1528                             scaledWidth = iconRealWidth;
    1529                             scaledHeight = iconRealHeight;
    1530                             scaledIcon = iconImage;
    1531                         }
    1532                         image.getGraphics().drawImage(scaledIcon, (backgroundRealWidth - scaledWidth) / 2,
    1533                                 (backgroundRealHeight - scaledHeight) / 2, null);
    1534 
    1535                         return new ImageIcon(image);
    1536                     }
    1537                 }
     1505            ImageIcon icon = getTaggedPadded(primitive, iconSize);
     1506            if (icon != null) {
     1507                return icon;
    15381508            }
    15391509        }
     
    15561526        // Use generic default icon.
    15571527        return ImageProvider.get(primitive.getDisplayType());
     1528    }
     1529
     1530    /**
     1531     * Computes a new padded icon for the given tagged primitive, using map paint styles.
     1532     * This is a slow operation.
     1533     * @param primitive tagged OSM primitive
     1534     * @param iconSize icon size in pixels
     1535     * @return a new padded icon for the given tagged primitive, or null
     1536     */
     1537    private static ImageIcon getTaggedPadded(OsmPrimitive primitive, Dimension iconSize) {
     1538        Pair<StyleElementList, Range> nodeStyles;
     1539        DataSet ds = primitive.getDataSet();
     1540        if (ds != null) {
     1541            ds.getReadLock().lock();
     1542        }
     1543        try {
     1544            nodeStyles = MapPaintStyles.getStyles().generateStyles(primitive, 100, false);
     1545        } finally {
     1546            if (ds != null) {
     1547                ds.getReadLock().unlock();
     1548            }
     1549        }
     1550        for (StyleElement style : nodeStyles.a) {
     1551            if (style instanceof NodeElement) {
     1552                NodeElement nodeStyle = (NodeElement) style;
     1553                MapImage icon = nodeStyle.mapImage;
     1554                if (icon != null) {
     1555                    return getPaddedIcon(icon, iconSize);
     1556                }
     1557            }
     1558        }
     1559        return null;
     1560    }
     1561
     1562    /**
     1563     * Returns an {@link ImageIcon} for the given map image, at the specified size.
     1564     * Uses a cache to improve performance.
     1565     * @param mapImage map image
     1566     * @param iconSize size in pixels
     1567     * @return an {@code ImageIcon} for the given map image, at the specified size
     1568     * @see #clearCache
     1569     * @since 14284
     1570     */
     1571    public static ImageIcon getPaddedIcon(MapImage mapImage, Dimension iconSize) {
     1572        synchronized (paddedImageCache) {
     1573            return paddedImageCache.computeIfAbsent(iconSize, x -> new HashMap<>()).computeIfAbsent(mapImage, icon -> {
     1574                int backgroundRealWidth = GuiSizesHelper.getSizeDpiAdjusted(iconSize.width);
     1575                int backgroundRealHeight = GuiSizesHelper.getSizeDpiAdjusted(iconSize.height);
     1576                int iconRealWidth = icon.getWidth();
     1577                int iconRealHeight = icon.getHeight();
     1578                BufferedImage image = new BufferedImage(backgroundRealWidth, backgroundRealHeight, BufferedImage.TYPE_INT_ARGB);
     1579                double scaleFactor = Math.min(
     1580                        backgroundRealWidth / (double) iconRealWidth,
     1581                        backgroundRealHeight / (double) iconRealHeight);
     1582                Image iconImage = icon.getImage(false);
     1583                Image scaledIcon;
     1584                final int scaledWidth;
     1585                final int scaledHeight;
     1586                if (scaleFactor < 1) {
     1587                    // Scale icon such that it fits on background.
     1588                    scaledWidth = (int) (iconRealWidth * scaleFactor);
     1589                    scaledHeight = (int) (iconRealHeight * scaleFactor);
     1590                    scaledIcon = iconImage.getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
     1591                } else {
     1592                    // Use original size, don't upscale.
     1593                    scaledWidth = iconRealWidth;
     1594                    scaledHeight = iconRealHeight;
     1595                    scaledIcon = iconImage;
     1596                }
     1597                image.getGraphics().drawImage(scaledIcon,
     1598                        (backgroundRealWidth - scaledWidth) / 2,
     1599                        (backgroundRealHeight - scaledHeight) / 2, null);
     1600
     1601                return new ImageIcon(image);
     1602            });
     1603        }
    15581604    }
    15591605
Note: See TracChangeset for help on using the changeset viewer.