package org.hipi.image.io;
import org.hipi.image.HipiImageHeader;
import org.hipi.image.HipiImageHeader.HipiImageFormat;
import org.hipi.image.HipiImageHeader.HipiColorSpace;
import org.hipi.image.HipiImage;
import org.hipi.image.HipiImage.HipiImageType;
import org.hipi.image.RasterImage;
import org.hipi.image.HipiImageFactory;
import org.hipi.image.PixelArray;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.HashMap;
import javax.imageio.IIOImage;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
/**
* Extends {@link ImageCodec} and serves as both an {@link ImageDecoder} and
* {@link ImageEncoder} for the JPEG image storage format.
*/
public class JpegCodec extends ImageCodec {
private static final JpegCodec staticObject = new JpegCodec();
public static JpegCodec getInstance() {
return staticObject;
}
public HipiImageHeader decodeHeader(InputStream inputStream, boolean includeExifData)
throws IOException, IllegalArgumentException {
DataInputStream dis = new DataInputStream(new BufferedInputStream(inputStream));
dis.mark(Integer.MAX_VALUE);
// all JPEGs start with -40
short magic = dis.readShort();
if (magic != -40)
return null;
int width=0, height=0, depth=0;
byte[] data = new byte[6];
// read in each block to determine resolution and bit depth
for (;;) {
dis.read(data, 0, 4);
if ((data[0] & 0xff) != 0xff)
return null;
if ((data[1] & 0xff) == 0x01 || ((data[1] & 0xff) >= 0xd0 && (data[1] & 0xff) <= 0xd7))
continue;
long length = (((data[2] & 0xff) << 8) | (data[3] & 0xff)) - 2;
if ((data[1] & 0xff) == 0xc0 || (data[1] & 0xff) == 0xc2) {
dis.read(data);
height = ((data[1] & 0xff) << 8) | (data[2] & 0xff);
width = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
depth = data[0] & 0xff;
break;
} else {
while (length > 0) {
long skipped = dis.skip(length);
if (skipped == 0)
break;
length -= skipped;
}
}
}
if (depth != 8) {
throw new IllegalArgumentException(String.format("Image has unsupported bit depth [%d].", depth));
}
HashMap<String,String> exifData = null;
if (includeExifData) {
dis.reset();
exifData = ExifDataReader.extractAndFlatten(dis);
}
return new HipiImageHeader(HipiImageFormat.JPEG, HipiColorSpace.RGB,
width, height, 3, null, exifData);
}
public void encodeImage(HipiImage image, OutputStream outputStream) throws IllegalArgumentException, IOException {
if (!(RasterImage.class.isAssignableFrom(image.getClass()))) {
throw new IllegalArgumentException("JPEG encoder supports only RasterImage input types.");
}
if (image.getWidth() <= 0 || image.getHeight() <= 0) {
throw new IllegalArgumentException("Invalid image resolution.");
}
if (image.getColorSpace() != HipiColorSpace.RGB) {
throw new IllegalArgumentException("JPEG encoder supports only RGB color space.");
}
if (image.getNumBands() != 3) {
throw new IllegalArgumentException("JPEG encoder supports only three band images.");
}
// Find suitable JPEG writer in javax.imageio.ImageReader
ImageOutputStream ios = ImageIO.createImageOutputStream(outputStream);
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter writer = writers.next();
System.out.println("Using JPEG encoder: " + writer);
writer.setOutput(ios);
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.95F); // highest JPEG quality = 1.0F
encodeRasterImage((RasterImage)image, writer, param);
}
}