Ignore:
Timestamp:
2020-11-26T22:24:27+01:00 (3 years ago)
Author:
simon04
Message:

see #20141 - ImageProvider: cache rendered SVG images using JCS

This experimental feature is disabled by default. Set the advanced preference jcs.cache.use_image_resource_cache=true to enable. No cache eviction for altered images is implemented at the moment.

Location:
trunk/src/org/openstreetmap/josm/tools
Files:
2 edited

Legend:

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

    r17144 r17364  
    954954                        continue;
    955955                    }
    956                     ir = getIfAvailableLocalURL(path, type);
     956                    ir = getIfAvailableLocalURL(subdir + name, path, type);
    957957                    if (ir != null) {
    958958                        cache.put(cacheName, ir);
     
    984984                    svg = getSvgUniverse().getDiagram(uri);
    985985                }
    986                 return svg == null ? null : new ImageResource(svg);
     986                return svg == null ? null : new ImageResource(url, svg);
    987987            case OTHER:
    988988                BufferedImage img = null;
     
    992992                    Logging.log(Logging.LEVEL_WARN, "Exception while reading HTTP image:", e);
    993993                }
    994                 return img == null ? null : new ImageResource(img);
     994                return img == null ? null : new ImageResource(url, img);
    995995            default:
    996996                throw new AssertionError("Unsupported type: " + type);
     
    10411041                    return null;
    10421042                }
    1043                 return new ImageResource(svg);
     1043                return new ImageResource(url, svg);
    10441044            } else {
    10451045                try {
     
    10501050                    // CHECKSTYLE.ON: LineLength
    10511051                    Image img = read(new ByteArrayInputStream(bytes), false, true);
    1052                     return img == null ? null : new ImageResource(img);
     1052                    return img == null ? null : new ImageResource(url, img);
    10531053                } catch (IOException | UnsatisfiedLinkError e) {
    10541054                    Logging.log(Logging.LEVEL_WARN, "Exception while reading image:", e);
     
    11251125                            svg = getSvgUniverse().getDiagram(uri);
    11261126                        }
    1127                         return svg == null ? null : new ImageResource(svg);
     1127                        return svg == null ? null : new ImageResource(fullName, svg);
    11281128                    case OTHER:
    11291129                        while (size > 0) {
     
    11381138                            Logging.warn(e);
    11391139                        }
    1140                         return img == null ? null : new ImageResource(img);
     1140                        return img == null ? null : new ImageResource(fullName, img);
    11411141                    default:
    11421142                        throw new AssertionError("Unknown ImageType: "+type);
     
    11571157     * @return the requested image or null if the request failed
    11581158     */
    1159     private static ImageResource getIfAvailableLocalURL(URL path, ImageType type) {
     1159    private static ImageResource getIfAvailableLocalURL(String cacheKey, URL path, ImageType type) {
    11601160        switch (type) {
    11611161        case SVG:
    1162             SVGDiagram svg = null;
    1163             synchronized (getSvgUniverse()) {
    1164                 try {
    1165                     URI uri = null;
     1162            return new ImageResource(cacheKey, () -> {
     1163                synchronized (getSvgUniverse()) {
    11661164                    try {
    1167                         uri = getSvgUniverse().loadSVG(path);
    1168                     } catch (InvalidPathException e) {
    1169                         Logging.error("Cannot open {0}: {1}", path, e.getMessage());
    1170                         Logging.trace(e);
     1165                        URI uri = null;
     1166                        try {
     1167                            uri = getSvgUniverse().loadSVG(path);
     1168                        } catch (InvalidPathException e) {
     1169                            Logging.error("Cannot open {0}: {1}", path, e.getMessage());
     1170                            Logging.trace(e);
     1171                        }
     1172                        if (uri == null && "jar".equals(path.getProtocol())) {
     1173                            URL betterPath = Utils.betterJarUrl(path);
     1174                            if (betterPath != null) {
     1175                                uri = getSvgUniverse().loadSVG(betterPath);
     1176                            }
     1177                        }
     1178                        return getSvgUniverse().getDiagram(uri);
     1179                    } catch (SecurityException | IOException e) {
     1180                        Logging.log(Logging.LEVEL_WARN, "Unable to read SVG", e);
    11711181                    }
    1172                     if (uri == null && "jar".equals(path.getProtocol())) {
    1173                         URL betterPath = Utils.betterJarUrl(path);
    1174                         if (betterPath != null) {
    1175                             uri = getSvgUniverse().loadSVG(betterPath);
    1176                         }
    1177                     }
    1178                     svg = getSvgUniverse().getDiagram(uri);
    1179                 } catch (SecurityException | IOException e) {
    1180                     Logging.log(Logging.LEVEL_WARN, "Unable to read SVG", e);
    1181                 }
    1182             }
    1183             return svg == null ? null : new ImageResource(svg);
     1182                }
     1183                return null;
     1184            });
    11841185        case OTHER:
    11851186            BufferedImage img = null;
     
    11961197                Logging.debug(e);
    11971198            }
    1198             return img == null ? null : new ImageResource(img);
     1199            return img == null ? null : new ImageResource(path.toString(), img);
    11991200        default:
    12001201            throw new AssertionError();
     
    13941395     */
    13951396    public static Image createBoundedImage(Image img, int maxSize) {
    1396         return new ImageResource(img).getImageIconBounded(new Dimension(maxSize, maxSize)).getImage();
     1397        return new ImageResource(img.toString(), img).getImageIconBounded(new Dimension(maxSize, maxSize)).getImage();
    13971398    }
    13981399
     
    19331934    }
    19341935
     1936    /**
     1937     * Prints statistics concerning the image loading caches.
     1938     */
     1939    public static void printStatistics() {
     1940        Logging.info(getSvgUniverse().statistics());
     1941        Logging.info(ImageResource.statistics());
     1942    }
     1943
    19351944    @Override
    19361945    public String toString() {
  • trunk/src/org/openstreetmap/josm/tools/ImageResource.java

    r17144 r17364  
    55import java.awt.Image;
    66import java.awt.image.BufferedImage;
     7import java.io.IOException;
     8import java.io.UncheckedIOException;
    79import java.util.List;
    8 import java.util.Map;
    9 import java.util.concurrent.ConcurrentHashMap;
     10import java.util.Locale;
     11import java.util.Objects;
     12import java.util.function.Supplier;
    1013
    1114import javax.swing.AbstractAction;
     
    1518import javax.swing.JPanel;
    1619import javax.swing.UIManager;
     20
     21import org.apache.commons.jcs3.access.behavior.ICacheAccess;
     22import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
     23import org.openstreetmap.josm.data.cache.JCSCacheManager;
    1724
    1825import com.kitfox.svg.SVGDiagram;
     
    2936
    3037    /**
    31      * Caches the image data for resized versions of the same image. The key is obtained using {@link ImageResizeMode#cacheKey(Dimension)}.
    32      */
    33     private final Map<Integer, BufferedImage> imgCache = new ConcurrentHashMap<>(4);
     38     * Caches the image data for resized versions of the same image.
     39     * Depending on {@link JCSCacheManager#USE_IMAGE_RESOURCE_CACHE}, a combined memory/disk, or an in-memory-cache is used.
     40     */
     41    private static final ICacheAccess<String, BufferedImageCacheEntry> imgCache = JCSCacheManager.getImageResourceCache();
     42
     43    private final String cacheKey;
    3444    /**
    3545     * SVG diagram information in case of SVG vector image.
     
    3747    private SVGDiagram svg;
    3848    /**
     49     * Supplier for SVG diagram information in case of possibly cached SVG vector image.
     50     */
     51    private Supplier<SVGDiagram> svgSupplier;
     52    /**
    3953     * Use this dimension to request original file dimension.
    4054     */
     
    5569    /**
    5670     * Constructs a new {@code ImageResource} from an image.
     71     * @param cacheKey the caching identifier of the image
    5772     * @param img the image
    5873     */
    59     public ImageResource(Image img) {
    60         CheckParameterUtil.ensureParameterNotNull(img);
    61         baseImage = img;
     74    public ImageResource(String cacheKey, Image img) {
     75        this.cacheKey = Objects.requireNonNull(cacheKey);
     76        this.baseImage = Objects.requireNonNull(img);
    6277    }
    6378
    6479    /**
    6580     * Constructs a new {@code ImageResource} from SVG data.
     81     * @param cacheKey the caching identifier of the image
    6682     * @param svg SVG data
    6783     */
    68     public ImageResource(SVGDiagram svg) {
    69         CheckParameterUtil.ensureParameterNotNull(svg);
    70         this.svg = svg;
     84    public ImageResource(String cacheKey, SVGDiagram svg) {
     85        this.cacheKey = Objects.requireNonNull(cacheKey);
     86        this.svg = Objects.requireNonNull(svg);
     87    }
     88
     89    public ImageResource(String cacheKey, Supplier<SVGDiagram> svgSupplier) {
     90        this.cacheKey = Objects.requireNonNull(cacheKey);
     91        this.svgSupplier = Objects.requireNonNull(svgSupplier);
    7192    }
    7293
     
    7899     */
    79100    public ImageResource(ImageResource res, List<ImageOverlay> overlayInfo) {
     101        this.cacheKey = res.cacheKey;
    80102        this.svg = res.svg;
     103        this.svgSupplier = res.svgSupplier;
    81104        this.baseImage = res.baseImage;
    82105        this.overlayInfo = overlayInfo;
     
    157180    ImageIcon getImageIcon(Dimension dim, boolean multiResolution, ImageResizeMode resizeMode) {
    158181        return getImageIconAlreadyScaled(GuiSizesHelper.getDimensionDpiAdjusted(dim), multiResolution, false, resizeMode);
     182    }
     183
     184    private BufferedImage getImageFromCache(String cacheKey) {
     185        try {
     186            BufferedImageCacheEntry image = imgCache.get(cacheKey);
     187            if (image == null || image.getImage() == null) {
     188                return null;
     189            }
     190            Logging.trace("{0} is in cache :-)", cacheKey);
     191            return image.getImage();
     192        } catch (IOException e) {
     193            throw new UncheckedIOException(e);
     194        }
    159195    }
    160196
     
    181217            resizeMode = ImageResizeMode.BOUNDED;
    182218        }
    183         final int cacheKey = resizeMode.cacheKey(dim);
    184         BufferedImage img = imgCache.get(cacheKey);
     219        final String cacheKey = String.format(Locale.ROOT, "%s--%s--%d--%d",
     220                this.cacheKey, resizeMode.name(), dim.width, dim.height);
     221        BufferedImage img = getImageFromCache(cacheKey);
    185222        if (img == null) {
     223            if (svgSupplier != null) {
     224                svg = svgSupplier.get();
     225                Logging.trace("{0} is not in cache :-(", cacheKey);
     226                svgSupplier = null;
     227            }
    186228            if (svg != null) {
    187229                img = ImageProvider.createImageFromSvg(svg, dim, resizeMode);
     
    211253                disabledIcon.paintIcon(new JPanel(), img.getGraphics(), 0, 0);
    212254            }
    213             imgCache.put(cacheKey, img);
     255            if (img == null) {
     256                return null;
     257            }
     258            BufferedImageCacheEntry cacheEntry = BufferedImageCacheEntry.pngEncoded(img);
     259            Logging.trace("Storing {0} ({1} bytes) in cache...", cacheKey, cacheEntry.getContent().length);
     260            imgCache.put(cacheKey, cacheEntry);
    214261        }
    215262
     
    251298    }
    252299
     300    static String statistics() {
     301        return String.format("ImageResource cache: [%s]", imgCache.getStatistics());
     302    }
     303
    253304    @Override
    254305    public String toString() {
Note: See TracChangeset for help on using the changeset viewer.