/* * #%L * BSD implementations of Bio-Formats readers and writers * %% * Copyright (C) 2005 - 2015 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package loci.formats.codec; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; import javax.imageio.ImageIO; import loci.common.DataTools; import loci.common.RandomAccessInputStream; import loci.formats.FormatException; import loci.formats.gui.AWTImageTools; /** * This class implements JPEG compression and decompression. */ public class JPEGCodec extends BaseCodec { /** * The CodecOptions parameter should have the following fields set: * {@link CodecOptions#width width} * {@link CodecOptions#height height} * {@link CodecOptions#channels channels} * {@link CodecOptions#bitsPerSample bitsPerSample} * {@link CodecOptions#interleaved interleaved} * {@link CodecOptions#littleEndian littleEndian} * {@link CodecOptions#signed signed} * * @see Codec#compress(byte[], CodecOptions) */ @Override public byte[] compress(byte[] data, CodecOptions options) throws FormatException { if (data == null || data.length == 0) return data; if (options == null) options = CodecOptions.getDefaultOptions(); if (options.bitsPerSample > 8) { throw new FormatException("> 8 bit data cannot be compressed with JPEG."); } ByteArrayOutputStream out = new ByteArrayOutputStream(); BufferedImage img = AWTImageTools.makeImage(data, options.width, options.height, options.channels, options.interleaved, options.bitsPerSample / 8, false, options.littleEndian, options.signed); try { ImageIO.write(img, "jpeg", out); } catch (IOException e) { throw new FormatException("Could not write JPEG data", e); } return out.toByteArray(); } /** * The CodecOptions parameter should have the following fields set: * {@link CodecOptions#interleaved interleaved} * {@link CodecOptions#littleEndian littleEndian} * * @see Codec#decompress(RandomAccessInputStream, CodecOptions) */ @Override public byte[] decompress(RandomAccessInputStream in, CodecOptions options) throws FormatException, IOException { BufferedImage b; long fp = in.getFilePointer(); try { try { while (in.read() != (byte) 0xff || in.read() != (byte) 0xd8); in.seek(in.getFilePointer() - 2); } catch (EOFException e) { in.seek(fp); } b = ImageIO.read(new BufferedInputStream(new DataInputStream(in), 8192)); } catch (IOException exc) { // probably a lossless JPEG; delegate to LosslessJPEGCodec in.seek(fp); return new LosslessJPEGCodec().decompress(in, options); } if (options == null) options = CodecOptions.getDefaultOptions(); byte[][] buf = AWTImageTools.getPixelBytes(b, options.littleEndian); // correct for YCbCr encoding, if necessary if (options.ycbcr && buf.length == 3) { int nBytes = buf[0].length / (b.getWidth() * b.getHeight()); int mask = (int) (Math.pow(2, nBytes * 8) - 1); for (int i=0; i<buf[0].length; i+=nBytes) { double y = DataTools.bytesToInt(buf[0], i, nBytes, options.littleEndian); double cb = DataTools.bytesToInt(buf[1], i, nBytes, options.littleEndian); double cr = DataTools.bytesToInt(buf[2], i, nBytes, options.littleEndian); cb = Math.max(0, cb - 128); cr = Math.max(0, cr - 128); int red = (int) (y + 1.402 * cr); int green = (int) (y - 0.34414 * cb - 0.71414 * cr); int blue = (int) (y + 1.772 * cb); red = (int) (Math.min(red, mask)) & mask; green = (int) (Math.min(green, mask)) & mask; blue = (int) (Math.min(blue, mask)) & mask; DataTools.unpackBytes(red, buf[0], i, nBytes, options.littleEndian); DataTools.unpackBytes(green, buf[1], i, nBytes, options.littleEndian); DataTools.unpackBytes(blue, buf[2], i, nBytes, options.littleEndian); } } byte[] rtn = new byte[buf.length * buf[0].length]; if (buf.length == 1) rtn = buf[0]; else { if (options.interleaved) { int next = 0; for (int i=0; i<buf[0].length; i++) { for (int j=0; j<buf.length; j++) { rtn[next++] = buf[j][i]; } } } else { for (int i=0; i<buf.length; i++) { System.arraycopy(buf[i], 0, rtn, i*buf[0].length, buf[i].length); } } } return rtn; } }