1 package com.hammurapi.jcapture; 2 3 import java.awt.image.BufferedImage; 4 import java.io.ByteArrayInputStream; 5 import java.io.ByteArrayOutputStream; 6 import java.io.IOException; 7 import java.lang.ref.Reference; 8 import java.lang.ref.SoftReference; 9 import java.nio.ByteBuffer; 10 import java.nio.MappedByteBuffer; 11 import java.nio.channels.FileChannel; 12 import java.nio.channels.FileChannel.MapMode; 13 import java.util.zip.Adler32; 14 15 import javax.imageio.ImageIO; 16 17 /** 18 * Mapped image is softly kept in memory and also is written to a temporary file. 19 * If image reference is cleared by the garbage collector, the image is loaded from the file on demand. 20 * @author Pavel 21 * 22 */ 23 public class MappedImage { 24 25 private Reference<BufferedImage> imageRef; 26 private Reference<byte[]> imageBytesRef; 27 private MappedByteBuffer buffer; 28 private int height; 29 private int width; 30 private String format; 31 private long checksum; 32 private int bytesLength; 33 MappedImage(final BufferedImage image, String format, FileChannel channel)34 public MappedImage(final BufferedImage image, String format, FileChannel channel) throws IOException { 35 if (format==null) { 36 throw new NullPointerException("Format is null"); 37 } 38 39 class HardReference extends SoftReference<BufferedImage> { 40 41 HardReference(BufferedImage referent) { 42 super(referent); 43 } 44 45 @Override 46 public BufferedImage get() { 47 return image; 48 } 49 50 } 51 imageRef = channel==null ? new HardReference(image) : new SoftReference<BufferedImage>(image); 52 width = image.getWidth(); 53 height = image.getHeight(); 54 this.format = format; 55 if (channel!=null) { 56 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 57 ImageIO.write(imageRef.get(), format, baos); 58 baos.close(); 59 byte[] imageBytes = baos.toByteArray(); 60 Adler32 adler = new Adler32(); 61 adler.update(imageBytes); 62 checksum = adler.getValue(); 63 bytesLength = imageBytes.length; 64 imageBytesRef = new SoftReference<byte[]>(imageBytes); 65 synchronized (channel) { 66 long position = channel.position(); 67 channel.write(ByteBuffer.wrap(imageBytes)); 68 buffer = channel.map(MapMode.READ_ONLY, position, imageBytes.length); 69 } 70 } 71 } 72 getImageBytes()73 public byte[] getImageBytes() throws IOException { 74 if (imageBytesRef==null) { 75 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 76 ImageIO.write(imageRef.get(), format, baos); 77 return baos.toByteArray(); 78 } 79 byte[] ret = imageBytesRef.get(); 80 if (ret==null) { 81 buffer.load(); 82 buffer.rewind(); 83 ret = new byte[buffer.remaining()]; 84 buffer.get(ret); 85 if (bytesLength != ret.length) { 86 throw new IllegalStateException("Invalid image bytes length, expected "+bytesLength+", got "+ret.length); 87 } 88 89 Adler32 adler = new Adler32(); 90 adler.update(ret); 91 if (checksum != adler.getValue()) { 92 throw new IllegalStateException("Invalid image bytes checksum"); 93 } 94 imageBytesRef = new SoftReference<byte[]>(ret); 95 } 96 return ret; 97 } 98 99 /** 100 * Reads from reference, if reference was cleared, loads from the mapped buffer. 101 * @return 102 * @throws IOException 103 */ getImage()104 public BufferedImage getImage() throws IOException { 105 BufferedImage ret = imageRef.get(); 106 if (ret==null) { 107 ret = ImageIO.read(new ByteArrayInputStream(getImageBytes())); 108 imageRef = new SoftReference<BufferedImage>(ret); 109 } 110 return ret; 111 } 112 getHeight()113 public int getHeight() { 114 return height; 115 } 116 getWidth()117 public int getWidth() { 118 return width; 119 } 120 121 } 122