1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.chimpchat.core; 17 18 import java.awt.Graphics; 19 import java.awt.image.BufferedImage; 20 import java.io.ByteArrayOutputStream; 21 import java.io.File; 22 import java.io.IOException; 23 import java.lang.ref.WeakReference; 24 import java.util.Iterator; 25 import java.util.logging.Level; 26 import java.util.logging.Logger; 27 28 import javax.imageio.ImageIO; 29 import javax.imageio.ImageWriter; 30 import javax.imageio.stream.ImageOutputStream; 31 32 /** 33 * Base class with basic functionality for ChimpImage implementations. 34 */ 35 public abstract class ChimpImageBase implements IChimpImage { 36 private static Logger LOG = Logger.getLogger(ChimpImageBase.class.getCanonicalName()); 37 38 /** 39 * Convert the ChimpImage to a BufferedImage. 40 * 41 * @return a BufferedImage for this ChimpImage. 42 */ 43 @Override 44 public abstract BufferedImage createBufferedImage(); 45 46 // Cache the BufferedImage so we don't have to generate it every time. 47 private WeakReference<BufferedImage> cachedBufferedImage = null; 48 49 /** 50 * Utility method to handle getting the BufferedImage and managing the cache. 51 * 52 * @return the BufferedImage for this image. 53 */ 54 @Override 55 public BufferedImage getBufferedImage() { 56 // Check the cache first 57 if (cachedBufferedImage != null) { 58 BufferedImage img = cachedBufferedImage.get(); 59 if (img != null) { 60 return img; 61 } 62 } 63 64 // Not in the cache, so create it and cache it. 65 BufferedImage img = createBufferedImage(); 66 cachedBufferedImage = new WeakReference<BufferedImage>(img); 67 return img; 68 } 69 70 @Override 71 public byte[] convertToBytes(String format) { 72 BufferedImage argb = convertSnapshot(); 73 74 ByteArrayOutputStream os = new ByteArrayOutputStream(); 75 try { 76 ImageIO.write(argb, format, os); 77 } catch (IOException e) { 78 return new byte[0]; 79 } 80 return os.toByteArray(); 81 } 82 83 @Override 84 public boolean writeToFile(String path, String format) { 85 if (format != null) { 86 return writeToFileHelper(path, format); 87 } 88 int offset = path.lastIndexOf('.'); 89 if (offset < 0) { 90 return writeToFileHelper(path, "png"); 91 } 92 String ext = path.substring(offset + 1); 93 Iterator<ImageWriter> writers = ImageIO.getImageWritersBySuffix(ext); 94 if (!writers.hasNext()) { 95 return writeToFileHelper(path, "png"); 96 } 97 ImageWriter writer = writers.next(); 98 BufferedImage image = convertSnapshot(); 99 try { 100 File f = new File(path); 101 f.delete(); 102 103 ImageOutputStream outputStream = ImageIO.createImageOutputStream(f); 104 writer.setOutput(outputStream); 105 106 try { 107 writer.write(image); 108 } finally { 109 writer.dispose(); 110 outputStream.flush(); 111 } 112 } catch (IOException e) { 113 return false; 114 } 115 return true; 116 } 117 118 @Override 119 public int getPixel(int x, int y) { 120 BufferedImage image = getBufferedImage(); 121 return image.getRGB(x, y); 122 } 123 124 private BufferedImage convertSnapshot() { 125 BufferedImage image = getBufferedImage(); 126 127 // Convert the image to ARGB so ImageIO writes it out nicely 128 BufferedImage argb = new BufferedImage(image.getWidth(), image.getHeight(), 129 BufferedImage.TYPE_INT_ARGB); 130 Graphics g = argb.createGraphics(); 131 g.drawImage(image, 0, 0, null); 132 g.dispose(); 133 return argb; 134 } 135 136 private boolean writeToFileHelper(String path, String format) { 137 BufferedImage argb = convertSnapshot(); 138 139 try { 140 ImageIO.write(argb, format, new File(path)); 141 } catch (IOException e) { 142 return false; 143 } 144 return true; 145 } 146 147 @Override 148 public boolean sameAs(IChimpImage other, double percent) { 149 BufferedImage otherImage = other.getBufferedImage(); 150 BufferedImage myImage = getBufferedImage(); 151 152 // Easy size check 153 if (otherImage.getWidth() != myImage.getWidth()) { 154 return false; 155 } 156 if (otherImage.getHeight() != myImage.getHeight()) { 157 return false; 158 } 159 160 int[] otherPixel = new int[1]; 161 int[] myPixel = new int[1]; 162 163 int width = myImage.getWidth(); 164 int height = myImage.getHeight(); 165 166 int numDiffPixels = 0; 167 // Now, go through pixel-by-pixel and check that the images are the same; 168 for (int y = 0; y < height; y++) { 169 for (int x = 0; x < width; x++) { 170 if (myImage.getRGB(x, y) != otherImage.getRGB(x, y)) { 171 numDiffPixels++; 172 } 173 } 174 } 175 double numberPixels = (height * width); 176 double diffPercent = numDiffPixels / numberPixels; 177 return percent <= 1.0 - diffPercent; 178 } 179 180 // TODO: figure out the location of this class and is superclasses 181 private static class BufferedImageChimpImage extends ChimpImageBase { 182 private final BufferedImage image; 183 184 public BufferedImageChimpImage(BufferedImage image) { 185 this.image = image; 186 } 187 188 @Override 189 public BufferedImage createBufferedImage() { 190 return image; 191 } 192 } 193 194 public static IChimpImage loadImageFromFile(String path) { 195 File f = new File(path); 196 if (f.exists() && f.canRead()) { 197 try { 198 BufferedImage bufferedImage = ImageIO.read(new File(path)); 199 if (bufferedImage == null) { 200 LOG.log(Level.WARNING, "Cannot decode file %s", path); 201 return null; 202 } 203 return new BufferedImageChimpImage(bufferedImage); 204 } catch (IOException e) { 205 LOG.log(Level.WARNING, "Exception trying to decode image", e); 206 return null; 207 } 208 } else { 209 LOG.log(Level.WARNING, "Cannot read file %s", path); 210 return null; 211 } 212 } 213 214 @Override 215 public IChimpImage getSubImage(int x, int y, int w, int h) { 216 BufferedImage image = getBufferedImage(); 217 return new BufferedImageChimpImage(image.getSubimage(x, y, w, h)); 218 } 219 } 220