/*
* Copyright 2001-2008 Geert Bevin <gbevin[remove] at uwyn dot com>
* Licensed under the Apache License, Version 2.0 (the "License")
* $Id: ImageFormatter.java 3918 2008-04-14 17:35:35Z gbevin $
*/
package com.uwyn.rife.cmf.format;
import com.uwyn.rife.cmf.Content;
import com.uwyn.rife.cmf.MimeType;
import com.uwyn.rife.cmf.format.exceptions.FormatException;
import com.uwyn.rife.cmf.format.exceptions.InvalidContentDataTypeException;
import com.uwyn.rife.cmf.format.exceptions.UnexpectedConversionErrorException;
import com.uwyn.rife.cmf.format.exceptions.UnreadableDataFormatException;
import com.uwyn.rife.cmf.format.exceptions.UnsupportedTargetMimeTypeException;
import com.uwyn.rife.cmf.loader.ImageContentLoader;
import com.uwyn.rife.cmf.transform.ContentTransformer;
import com.uwyn.rife.tools.ImageWaiter;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
/**
* Formats raw <code>Content</code> data as an image.
*
* <p>The following content attributes are supported:
* <table>
* <tr>
* <td valign="top"><code>width</code></td>
* <td>Changes the width of the image. If no height is provided, the image
* will be proportionally scaled.</td>
* </tr>
* <tr>
* <td valign="top"><code>height</code></td>
* <td>Changes the height of the image. If no width is provided, the image
* will be proportionally scaled.</td>
* </tr>
* <tr>
* <td valign="top"><code>longest-edge-length</code></td>
* <td>Changes the longest edge of the image. Aspect ratio is preserved. The "width" or
* "height" attributes take precendence if set, and this attribute will be ignored.
* </td>
* </tr>
* </table>
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @version $Revision: 3918 $
* @since 1.0
* @see Formatter
*/
public class ImageFormatter implements Formatter<byte[], Image>
{
public static final String CONTENT_ATTRIBUTE_WIDTH = "width";
public static final String CONTENT_ATTRIBUTE_HEIGHT = "height";
public static final String CONTENT_ATTRIBUTE_LONGEST_EDGE_LENGTH = "longestEdgeLength";
public static final String CMF_PROPERTY_WIDTH = "cmf:width";
public static final String CMF_PROPERTY_HEIGHT = "cmf:height";
public byte[] format(Content content, ContentTransformer<Image> transformer)
throws FormatException
{
if (!(content.getData() instanceof byte[]))
{
throw new InvalidContentDataTypeException(this, content.getMimeType(), byte[].class, content.getData().getClass());
}
Image data = null;
// check if the content contains a cached value of the loaded data
if (content.hasCachedLoadedData())
{
data = (Image)content.getCachedLoadedData();
}
if (null == data)
{
// get an image
Set<String> errors = new HashSet<String>();
data = new ImageContentLoader().load(content.getData(), false, errors);
if (null == data)
{
throw new UnreadableDataFormatException(content.getMimeType(), errors);
}
}
// perform additional conversions according to the provided attributes
if (content.hasAttributes())
{
int width = -1;
int height = -1;
if (content.hasAttribute(CONTENT_ATTRIBUTE_WIDTH) ||
content.hasAttribute(CONTENT_ATTRIBUTE_HEIGHT))
{
String width_value = content.getAttribute(CONTENT_ATTRIBUTE_WIDTH);
String height_value = content.getAttribute(CONTENT_ATTRIBUTE_HEIGHT);
// retrieve the width and the height values
if (width_value != null)
{
try
{
width = Integer.parseInt(width_value);
}
catch (NumberFormatException e)
{
throw new FormatException(e);
}
}
if (height_value != null)
{
try
{
height = Integer.parseInt(height_value);
}
catch (NumberFormatException e)
{
throw new FormatException(e);
}
}
}
else if (content.hasAttribute(CONTENT_ATTRIBUTE_LONGEST_EDGE_LENGTH))
{
// retrieve the attributes
String lel_value = content.getAttribute(CONTENT_ATTRIBUTE_LONGEST_EDGE_LENGTH);
int lel = -1;
if (lel_value != null)
{
try
{
lel = Integer.parseInt(lel_value);
}
catch (NumberFormatException e)
{
throw new FormatException(e);
}
}
if (lel >= 0)
{
int orig_width = data.getWidth(null);
int orig_height = data.getHeight(null);
// If the width is the longer side, set it to the lEL.
if (orig_width >= orig_height)
{
if (lel >= 0)
{
width = lel;
}
}
else
{
if (lel >= 0)
{
height = lel;
}
}
}
}
if (width >= 0 || height >= 0)
{
int orig_width = data.getWidth(null);
int orig_height = data.getHeight(null);
// ensure that the aspect is preserved at all times
if (width >= 0 && height >= 0)
{
double width_ratio = ((double)orig_width)/width;
double height_ratio = ((double)orig_height)/height;
if (width_ratio > height_ratio)
{
height = -1;
}
else if (width_ratio < height_ratio)
{
width = -1;
}
}
// only do a rescale when the dimensions are actually different
if ((width >= 0 && width != orig_width) ||
(height >= 0 && height != orig_height))
{
data = data.getScaledInstance(width, height, Image.SCALE_SMOOTH);
if (data.getWidth(null) < 0 ||
data.getHeight(null) < 0)
{
ImageWaiter.wait(data);
}
}
}
}
// transform the content, if needed
if (transformer != null)
{
data = transformer.transform(data, content.getAttributes());
}
// draw it on a new buffer
BufferedImage buffer = null;
if (content.getMimeType() == MimeType.IMAGE_JPEG)
{
buffer = new BufferedImage(data.getWidth(null), data.getHeight(null), BufferedImage.TYPE_INT_RGB);
}
else
{
buffer = new BufferedImage(data.getWidth(null), data.getHeight(null), BufferedImage.TYPE_INT_ARGB);
}
Graphics2D g2 = buffer.createGraphics();
g2.setComposite(AlphaComposite.SrcOver);
g2.drawImage(data, 0, 0, null);
g2.dispose();
// set the content data properties
content
.property(CMF_PROPERTY_WIDTH, String.valueOf(buffer.getWidth()))
.property(CMF_PROPERTY_HEIGHT, String.valueOf(buffer.getHeight()));
// write it out as the correct mimetype
ByteArrayOutputStream bytes_out = new ByteArrayOutputStream();
BufferedOutputStream buffered_out = new BufferedOutputStream(bytes_out);
try
{
// retrieve a supported writer
Iterator<ImageWriter> writers = ImageIO.getImageWritersByMIMEType(content.getMimeType().toString());
ImageWriter writer = null;
if (writers.hasNext())
{
writer = writers.next();
}
if (null == writer)
{
throw new UnsupportedTargetMimeTypeException(content.getMimeType());
}
ImageOutputStream image_out = ImageIO.createImageOutputStream(buffered_out);
writer.setOutput(image_out);
writer.write(buffer);
writer.dispose();
bytes_out.flush();
bytes_out.close();
}
catch (IOException e)
{
throw new UnexpectedConversionErrorException(e);
}
return bytes_out.toByteArray();
}
}