/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.chimpchat.core;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
/**
* Base class with basic functionality for ChimpImage implementations.
*/
public abstract class ChimpImageBase implements IChimpImage {
private static Logger LOG = Logger.getLogger(ChimpImageBase.class.getCanonicalName());
/**
* Convert the ChimpImage to a BufferedImage.
*
* @return a BufferedImage for this ChimpImage.
*/
@Override
public abstract BufferedImage createBufferedImage();
// Cache the BufferedImage so we don't have to generate it every time.
private WeakReference<BufferedImage> cachedBufferedImage = null;
/**
* Utility method to handle getting the BufferedImage and managing the cache.
*
* @return the BufferedImage for this image.
*/
@Override
public BufferedImage getBufferedImage() {
// Check the cache first
if (cachedBufferedImage != null) {
BufferedImage img = cachedBufferedImage.get();
if (img != null) {
return img;
}
}
// Not in the cache, so create it and cache it.
BufferedImage img = createBufferedImage();
cachedBufferedImage = new WeakReference<BufferedImage>(img);
return img;
}
@Override
public byte[] convertToBytes(String format) {
BufferedImage argb = convertSnapshot();
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ImageIO.write(argb, format, os);
} catch (IOException e) {
return new byte[0];
}
return os.toByteArray();
}
@Override
public boolean writeToFile(String path, String format) {
if (format != null) {
return writeToFileHelper(path, format);
}
int offset = path.lastIndexOf('.');
if (offset < 0) {
return writeToFileHelper(path, "png");
}
String ext = path.substring(offset + 1);
Iterator<ImageWriter> writers = ImageIO.getImageWritersBySuffix(ext);
if (!writers.hasNext()) {
return writeToFileHelper(path, "png");
}
ImageWriter writer = writers.next();
BufferedImage image = convertSnapshot();
try {
File f = new File(path);
f.delete();
ImageOutputStream outputStream = ImageIO.createImageOutputStream(f);
writer.setOutput(outputStream);
try {
writer.write(image);
} finally {
writer.dispose();
outputStream.flush();
}
} catch (IOException e) {
return false;
}
return true;
}
@Override
public int getPixel(int x, int y) {
BufferedImage image = getBufferedImage();
return image.getRGB(x, y);
}
private BufferedImage convertSnapshot() {
BufferedImage image = getBufferedImage();
// Convert the image to ARGB so ImageIO writes it out nicely
BufferedImage argb = new BufferedImage(image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics g = argb.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return argb;
}
private boolean writeToFileHelper(String path, String format) {
BufferedImage argb = convertSnapshot();
try {
ImageIO.write(argb, format, new File(path));
} catch (IOException e) {
return false;
}
return true;
}
@Override
public boolean sameAs(IChimpImage other, double percent) {
BufferedImage otherImage = other.getBufferedImage();
BufferedImage myImage = getBufferedImage();
// Easy size check
if (otherImage.getWidth() != myImage.getWidth()) {
return false;
}
if (otherImage.getHeight() != myImage.getHeight()) {
return false;
}
int[] otherPixel = new int[1];
int[] myPixel = new int[1];
int width = myImage.getWidth();
int height = myImage.getHeight();
int numDiffPixels = 0;
// Now, go through pixel-by-pixel and check that the images are the same;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (myImage.getRGB(x, y) != otherImage.getRGB(x, y)) {
numDiffPixels++;
}
}
}
double numberPixels = (height * width);
double diffPercent = numDiffPixels / numberPixels;
return percent <= 1.0 - diffPercent;
}
// TODO: figure out the location of this class and is superclasses
private static class BufferedImageChimpImage extends ChimpImageBase {
private final BufferedImage image;
public BufferedImageChimpImage(BufferedImage image) {
this.image = image;
}
@Override
public BufferedImage createBufferedImage() {
return image;
}
}
public static IChimpImage loadImageFromFile(String path) {
File f = new File(path);
if (f.exists() && f.canRead()) {
try {
BufferedImage bufferedImage = ImageIO.read(new File(path));
if (bufferedImage == null) {
LOG.log(Level.WARNING, "Cannot decode file %s", path);
return null;
}
return new BufferedImageChimpImage(bufferedImage);
} catch (IOException e) {
LOG.log(Level.WARNING, "Exception trying to decode image", e);
return null;
}
} else {
LOG.log(Level.WARNING, "Cannot read file %s", path);
return null;
}
}
@Override
public IChimpImage getSubImage(int x, int y, int w, int h) {
BufferedImage image = getBufferedImage();
return new BufferedImageChimpImage(image.getSubimage(x, y, w, h));
}
}