/* AImage.java Purpose: Description: History: Thu Feb 6 16:56:33 2003, Created by tomyeh Copyright (C) 2002 Potix Corporation. All Rights Reserved. {{IS_RIGHT This program is distributed under LGPL Version 2.1 in the hope that it will be useful, but WITHOUT ANY WARRANTY. }}IS_RIGHT */ package org.zkoss.image; import java.io.File; import java.io.InputStream; import java.io.FileInputStream; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.Reader; import java.io.IOException; import java.net.URL; import java.util.Iterator; import javax.swing.ImageIcon; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zkoss.io.Files; import org.zkoss.util.media.ContentTypes; /** * Represents an image. * Unlike java.awt.Image and javax.swing.ImageIcon, this class holds * the raw image data, i.e., the original format, as opaque. * * <p>In other words, it is used to retrieve and store the opaque data * as polymorphic thru the {@link org.zkoss.util.media.Media} interface. * * <p>To encode AWT image to an instance of {@link Image}, * use {@link Images#encode}. * * @author tomyeh * @see Images#encode */ public class AImage implements Image, java.io.Serializable { private static final Logger log = LoggerFactory.getLogger(AImage.class); /** The raw data. */ private byte[] _data; /** The format name, e.g., "jpeg", "gif" and "png". */ private String _format; /** The content type. */ private String _ctype; /** The name (usually filename). */ private String _name; /** The width. */ private int _width; /** The height. */ private int _height; /** the hash code. */ // private transient int _hashCode = 0; /** Constructs an image with a byte array. */ public AImage(String name, byte[] data) throws IOException { init(name, data); } /** Constructs an image with an input stream. * * <p>Note that this method automatically closes the input stream * (since ZK 3.0.0). */ public AImage(String name, InputStream is) throws IOException { try { init(name, Files.readAll(is)); } finally { is.close(); } } /** Constructs an image with a file name. */ public AImage(String filename) throws IOException { this(new File(filename)); } /** Constructs an image with a file. */ public AImage(File file) throws IOException { this(file.getName(), new BufferedInputStream(new FileInputStream(file))); } /** Constructs an image with an URL. */ public AImage(URL url) throws IOException { this(getName(url), url.openStream()); } private void init(String name, byte[] data) throws IOException { if (data == null) throw new IllegalArgumentException("null data"); _name = name; _data = data; //retrieve format String format = null; try { final ImageInputStream imis = ImageIO.createImageInputStream(new ByteArrayInputStream(data)); final Iterator it = ImageIO.getImageReaders(imis); if (it.hasNext()) { final ImageReader rd = (ImageReader)it.next(); format = rd.getFormatName().toLowerCase(java.util.Locale.ENGLISH); } } catch (IOException ex) { //not possible, but eat it and recover it later } if (format == null) { _format = getFormatByName(name); if (_format == null) throw new IOException("Unknown image format: "+name); log.warn("Unsupported image format: "+_format+"; its width and height are assumed to zero"); _width = _height = 0; } else { //recognized by J2SDK _format = format; final ImageIcon ii = new ImageIcon(_data); _width = ii.getIconWidth(); _height = ii.getIconHeight(); } _ctype = getContentType(_format); } private static String getName(URL url) { String name = url.getPath(); if (name != null) { { final int j = name.lastIndexOf(File.pathSeparatorChar); if (j >= 0) name = name.substring(j + 1); } if (File.pathSeparatorChar != '/') { final int j = name.lastIndexOf('/'); if (j >= 0) name = name.substring(j + 1); } } return name; } private static String getContentType(String format) { final String ctype = ContentTypes.getContentType(format); return ctype != null ? ctype: "image/" + format; } private static String getFormatByName(String name) { if (name != null) { final int j = name.lastIndexOf('.') + 1, k = name.lastIndexOf('/') + 1; if (j > k && j < name.length()) return name.substring(j); } return null; } //-- Media --// public final boolean isBinary() { return true; } public final boolean inMemory() { return true; //FUTURE: consider to support input stream } public byte[] getByteData() { return _data; } /** Always throws IllegalStateException. */ public final String getStringData() { throw new IllegalStateException("Use getByteData() instead"); } /** An input stream on top of {@link #getByteData}. * <p>Though harmless, the caller doesn't need to close the returned * stream. */ public final InputStream getStreamData() { return new ByteArrayInputStream(_data); } /** Always throws IllegalStateException. */ public final Reader getReaderData() { throw new IllegalStateException("Use getStreamData() instead"); } public final String getName() { return _name; } public final String getFormat() { return _format; } public final String getContentType() { return _ctype; } //-- Image --// /** Returns the width. */ public final int getWidth() { return _width; } /** Returns the height. */ public final int getHeight() { return _height; } /** Converts to an image icon. */ public final ImageIcon toImageIcon() { return new ImageIcon(_data, _format); } public boolean isContentDisposition() { return true; } //-- Object --// /* 20041014: Tom Yeh: Due to performance and usability, it is no sense to compare by content. public int hashCode() { if (_hashCode == 0) _hashCode = Objects.hashCode(_data, 16); return _hashCode; } public boolean equals(Object o) { if (!(o instanceof Image)) return false; final Image i = (Image)o; return _width == i.getWidth() && _height == i.getHeight() && Objects.equals(_format, i.getFormat()) && Objects.equals(_data, i.getByteData()); } */ }