Home | History | Annotate | Download | only in core
      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