/* This file is part of Cyclos (www.cyclos.org). A project of the Social Trade Organisation (www.socialtrade.org). Cyclos is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Cyclos is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Cyclos; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package nl.strohalm.cyclos.utils; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.List; import nl.strohalm.cyclos.exceptions.ApplicationException; import com.mullassery.imaging.Imaging; import com.mullassery.imaging.ImagingFactory; import com.mullassery.imaging.util.Util; /** * Contains helper methods for image manipulation * @author luis */ public final class ImageHelper { /** * Contains the known image types * @author luis */ public static enum ImageType { GIF(GIF_SIGNATURE, "image/gif"), ICO(null, "image/x-ico", "image/x-icon", "image/ico"), JPEG(JPEG_SIGNATURE, "image/jpeg", "image/pjpeg"), PNG(PNG_SIGNATURE, "image/png", "image/x-png"); /** * Try to identify a file type given the first content bytes */ public static ImageType getByContent(final byte[] firstBytes) { for (final ImageType type : values()) { final byte[] signature = type.getSignature(); if (signature == null || firstBytes.length < signature.length) { continue; } // The first bytes are the type signature boolean starts = true; for (int i = 0; i < signature.length; i++) { if (signature[i] != firstBytes[i]) { starts = false; break; } } if (starts) { return type; } } throw new UnknownImageTypeException(); } /** * Try to identify a file type by reading it's content */ public static ImageType getByContent(final File file) throws IOException { final FileInputStream in = new FileInputStream(file); try { final byte[] buffer = new byte[10]; in.read(buffer); return getByContent(buffer); } finally { in.close(); } } /** * Try to identify a file type given a MIME content type */ public static ImageType getByContentType(final String contentType) { for (final ImageType type : values()) { final List<String> types = Arrays.asList(type.contentTypes); if (types.contains(contentType)) { return type; } } throw new UnknownImageTypeException(); } /** * Try to identify a file type given a file name, using the extension */ public static ImageType getByFileName(final String fileName) { String extension; final int sep = fileName.lastIndexOf('.'); if (sep <= 0) { extension = ""; } else { extension = fileName.substring(sep + 1); } extension = extension.toLowerCase(); if (extension.equals("jpg") || extension.equals("jpeg")) { return JPEG; } else if (extension.equals("gif")) { return GIF; } else if (extension.equals("png")) { return PNG; } else if (extension.equals("ico")) { return ICO; } else if (extension.equals("png")) { return PNG; } throw new UnknownImageTypeException(); } private final byte[] signature; private final String[] contentTypes; private ImageType(final byte[] signature, final String... contentTypes) { this.signature = signature; this.contentTypes = contentTypes; } public String getContentType() { return contentTypes[0]; } public String[] getContentTypes() { return contentTypes; } public byte[] getSignature() { return signature; } /** * Returns whether this image type can be resized by the application */ public boolean isResizeSupported() { return this != ICO; } } /** * Exception thrown when trying to lookup an unknown image type * @author luis */ public static class UnknownImageTypeException extends ApplicationException { private static final long serialVersionUID = 8673642585560912420L; } private static final byte[] GIF_SIGNATURE = { (byte) 0x47, (byte) 0x49, (byte) 0x46 }; private static final byte[] JPEG_SIGNATURE = { (byte) 0xFF, (byte) 0xD8, (byte) 0xFF }; private static final byte[] PNG_SIGNATURE = { (byte) 0x89, (byte) 0x50, (byte) 0x4e, (byte) 0x47, (byte) 0x0d, (byte) 0x0a, (byte) 0x1a, (byte) 0x0a }; private static Imaging imaging; /** * @see #resizeGivenMaxDimensions(BufferedImage, String, Dimensions) */ public static File generateThumbnail(final BufferedImage image, final String contentType, final int maxWidth, final int maxHeight) throws IOException { return resizeGivenMaxDimensions(image, contentType, new Dimensions(maxWidth, maxHeight)); } /** * Loads an image from an stream */ public static BufferedImage load(final InputStream in) { return imaging().read(in); } /** * Resizes the given image to a maximum according to the given dimensions, returning the file where it is stored */ public static File resizeGivenMaxDimensions(final BufferedImage image, String contentType, final Dimensions maxDimensions) throws IOException, FileNotFoundException { final Dimensions originalDimensions = new Dimensions(image.getWidth(), image.getHeight()); if ((ImageHelper.ImageType.getByContentType(contentType) == ImageHelper.ImageType.GIF)) { contentType = "image/png"; } // Resize the image and write it to disk final Dimensions newDimensions = originalDimensions.resizeKeepingRatio(maxDimensions); final BufferedImage thumbnail = imaging().resize(image, newDimensions.getWidth(), newDimensions.getHeight()); final File thumbFile = File.createTempFile("cyclos", "image"); Util.saveImage(imaging, thumbnail, ImageType.getByContentType(contentType).name(), thumbFile); return thumbFile; } /** * Returns the imaging instance */ private synchronized static Imaging imaging() { if (imaging == null) { imaging = ImagingFactory.createImagingInstance(); } return imaging; } }