001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: ImageFormatter.java 3634 2007-01-08 21:42:24Z gbevin $
007: */
008: package com.uwyn.rife.cmf.format;
009:
010: import com.uwyn.rife.cmf.Content;
011: import com.uwyn.rife.cmf.MimeType;
012: import com.uwyn.rife.cmf.format.exceptions.FormatException;
013: import com.uwyn.rife.cmf.format.exceptions.InvalidContentDataTypeException;
014: import com.uwyn.rife.cmf.format.exceptions.UnexpectedConversionErrorException;
015: import com.uwyn.rife.cmf.format.exceptions.UnreadableDataFormatException;
016: import com.uwyn.rife.cmf.format.exceptions.UnsupportedTargetMimeTypeException;
017: import com.uwyn.rife.cmf.loader.ImageContentLoader;
018: import com.uwyn.rife.cmf.transform.ContentTransformer;
019: import com.uwyn.rife.tools.ImageWaiter;
020: import java.awt.AlphaComposite;
021: import java.awt.Graphics2D;
022: import java.awt.Image;
023: import java.awt.image.BufferedImage;
024: import java.io.BufferedOutputStream;
025: import java.io.ByteArrayOutputStream;
026: import java.io.IOException;
027: import java.util.HashSet;
028: import java.util.Iterator;
029: import java.util.Set;
030: import javax.imageio.ImageIO;
031: import javax.imageio.ImageWriter;
032: import javax.imageio.stream.ImageOutputStream;
033:
034: /**
035: * Formats raw <code>Content</code> data as an image.
036: * <p>The following content attributes are supported:
037: * <table>
038: * <tr>
039: * <td><code>width</code>
040: * <td>Changes the width of the image. If no height is provided, the image
041: * will be proportionally scaled.
042: * <tr>
043: * <td><code>height</code>
044: * <td>Changes the height of the image. If no width is provided, the image
045: * will be proportionally scaled.
046: * </table>
047: *
048: * @author Geert Bevin (gbevin[remove] at uwyn dot com)
049: * @version $Revision: 3634 $
050: * @since 1.0
051: * @see Formatter
052: */
053: public class ImageFormatter implements Formatter<byte[], Image> {
054: public byte[] format(Content content,
055: ContentTransformer<Image> transformer)
056: throws FormatException {
057: if (!(content.getData() instanceof byte[])) {
058: throw new InvalidContentDataTypeException(this , content
059: .getMimeType(), byte[].class, content.getData()
060: .getClass());
061: }
062:
063: Image data = null;
064:
065: // check if the content contains a cached value of the loaded data
066: if (content.hasCachedLoadedData()) {
067: data = (Image) content.getCachedLoadedData();
068: }
069:
070: if (null == data) {
071: // get an image
072: Set<String> errors = new HashSet<String>();
073: data = new ImageContentLoader().load(content.getData(),
074: false, errors);
075: if (null == data) {
076: throw new UnreadableDataFormatException(content
077: .getMimeType(), errors);
078: }
079: }
080:
081: // perform additional conversions according to the provided attributes
082: if (content.hasAttributes()) {
083: if (content.hasAttribute("width")
084: || content.hasAttribute("height")) {
085: // retrieve the width and the height values
086: String width_value = content.getAttribute("width");
087: String height_value = content.getAttribute("height");
088: int width = -1;
089: int height = -1;
090: if (width_value != null) {
091: try {
092: width = Integer.parseInt(width_value);
093: } catch (NumberFormatException e) {
094: throw new FormatException(e);
095: }
096: }
097: if (height_value != null) {
098: try {
099: height = Integer.parseInt(height_value);
100: } catch (NumberFormatException e) {
101: throw new FormatException(e);
102: }
103: }
104:
105: if (width >= 0 || height >= 0) {
106: int orig_width = data.getWidth(null);
107: int orig_height = data.getHeight(null);
108:
109: // ensure that the aspect is preserved at all times
110: if (width >= 0 && height >= 0) {
111: double width_ratio = ((double) orig_width)
112: / width;
113: double height_ratio = ((double) orig_height)
114: / height;
115: if (width_ratio > height_ratio) {
116: height = -1;
117: } else if (width_ratio < height_ratio) {
118: width = -1;
119: }
120: }
121:
122: // only do a rescale when the dimensions are actually different
123: if ((width >= 0 && width != orig_width)
124: || (height >= 0 && height != orig_height)) {
125: data = data.getScaledInstance(width, height,
126: Image.SCALE_SMOOTH);
127: if (data.getWidth(null) < 0
128: || data.getHeight(null) < 0) {
129: ImageWaiter.wait(data);
130: }
131: }
132: }
133: }
134: }
135:
136: // transform the content, if needed
137: if (transformer != null) {
138: data = transformer.transform(data, content.getAttributes());
139: }
140:
141: // draw it on a new buffer
142: BufferedImage buffer = null;
143: if (content.getMimeType() == MimeType.IMAGE_JPEG) {
144: buffer = new BufferedImage(data.getWidth(null), data
145: .getHeight(null), BufferedImage.TYPE_INT_RGB);
146: } else {
147: buffer = new BufferedImage(data.getWidth(null), data
148: .getHeight(null), BufferedImage.TYPE_INT_ARGB);
149: }
150: Graphics2D g2 = buffer.createGraphics();
151: g2.setComposite(AlphaComposite.SrcOver);
152: g2.drawImage(data, 0, 0, null);
153: g2.dispose();
154:
155: // set the content data properties
156: content
157: .property("cmf:width",
158: String.valueOf(buffer.getWidth())).property(
159: "cmf:height",
160: String.valueOf(buffer.getHeight()));
161:
162: // write it out as the correct mimetype
163: ByteArrayOutputStream bytes_out = new ByteArrayOutputStream();
164: BufferedOutputStream buffered_out = new BufferedOutputStream(
165: bytes_out);
166:
167: try {
168:
169: // retrieve a supported writer
170: Iterator<ImageWriter> writers = ImageIO
171: .getImageWritersByMIMEType(content.getMimeType()
172: .toString());
173: ImageWriter writer = null;
174: if (writers.hasNext()) {
175: writer = writers.next();
176: }
177: if (null == writer) {
178: throw new UnsupportedTargetMimeTypeException(content
179: .getMimeType());
180: }
181: ImageOutputStream image_out = ImageIO
182: .createImageOutputStream(buffered_out);
183: writer.setOutput(image_out);
184: writer.write(buffer);
185: writer.dispose();
186: bytes_out.flush();
187: bytes_out.close();
188: } catch (IOException e) {
189: throw new UnexpectedConversionErrorException(e);
190: }
191:
192: return bytes_out.toByteArray();
193: }
194: }
|