001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.cache; 003 004import java.awt.image.BufferedImage; 005import java.io.ByteArrayInputStream; 006import java.io.ByteArrayOutputStream; 007import java.io.IOException; 008import java.io.UncheckedIOException; 009 010import javax.imageio.ImageIO; 011 012/** 013 * Cache Entry that has methods to get the BufferedImage, that will be cached along in memory 014 * but will be not serialized when saved to the disk (to avoid duplication of data) 015 * 016 * @author Wiktor Niesiobędzki 017 */ 018public class BufferedImageCacheEntry extends CacheEntry { 019 private static final long serialVersionUID = 1L; //version 020 // transient to avoid serialization, volatile to avoid synchronization of whole getImage() method 021 private transient volatile BufferedImage img; 022 // we need to have separate control variable, to know, if we already tried to load the image, as img might be null 023 // after we loaded image, as for example, when image file is malformed (eg. HTML file) 024 private transient volatile boolean imageLoaded; 025 026 /** 027 * 028 * @param content byte array containing image 029 */ 030 public BufferedImageCacheEntry(byte[] content) { 031 super(content); 032 } 033 034 /** 035 * Encodes the given image as PNG and returns a cache entry 036 * @param img the image 037 * @return a cache entry for the PNG encoded image 038 * @throws UncheckedIOException if an I/O error occurs 039 */ 040 public static BufferedImageCacheEntry pngEncoded(BufferedImage img) { 041 try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { 042 ImageIO.write(img, "png", output); 043 return new BufferedImageCacheEntry(output.toByteArray()); 044 } catch (IOException e) { 045 throw new UncheckedIOException(e); 046 } 047 } 048 049 /** 050 * Returns BufferedImage from for the content. Subsequent calls will return the same instance, 051 * to reduce overhead of ImageIO 052 * 053 * @return BufferedImage of cache entry content 054 * @throws IOException if an error occurs during reading. 055 */ 056 public BufferedImage getImage() throws IOException { 057 if (imageLoaded) 058 return img; 059 synchronized (this) { 060 if (imageLoaded) 061 return img; 062 byte[] content = getContent(); 063 if (content.length > 0) { 064 img = ImageIO.read(new ByteArrayInputStream(content)); 065 imageLoaded = true; 066 } 067 } 068 return img; 069 } 070 071 private void writeObject(java.io.ObjectOutputStream out) throws IOException { 072 /* 073 * This method below will be needed, if Apache Commons JCS (or any other caching system), will update 074 * disk representation of object from memory, once it is put into the cache (for example - at closing the cache) 075 * 076 * For now it is not the case, as we use DiskUsagePattern.UPDATE, which on JCS shutdown doesn't write again memory 077 * contents to file, so the fact, that we've cleared never gets saved to the disk 078 * 079 * This method is commented out, as it will convert all cache entries to PNG files regardless of what was returned. 080 * It might cause recompression/change of format which may result in decreased quality of imagery 081 */ 082 /* synchronized (this) { 083 if (content == null && img != null) { 084 ByteArrayOutputStream restoredData = new ByteArrayOutputStream(); 085 ImageIO.write(img, "png", restoredData); 086 content = restoredData.toByteArray(); 087 } 088 out.writeObject(this); 089 } 090 */ 091 synchronized (this) { 092 if (content == null && img != null) { 093 throw new AssertionError("Trying to serialize (save to disk?) an BufferedImageCacheEntry " + 094 "that was converted to BufferedImage and no raw data is present anymore"); 095 } 096 out.writeObject(this); 097 098 if (img != null) { 099 content = null; 100 } 101 } 102 } 103}