Package com.alkacon.simapi.CmykJpegReader

Source Code of com.alkacon.simapi.CmykJpegReader.EXIFThumbnailReader

/*
* Copyright (c) 2012, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * 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.
*     * Neither the name "TwelveMonkeys" nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* 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 OWNER 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.
*/

package com.alkacon.simapi.CmykJpegReader;

import javax.imageio.IIOException;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.lang.ref.SoftReference;
import java.util.Arrays;

/**
* EXIFThumbnail
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: EXIFThumbnail.java,v 1.0 18.04.12 12:19 haraldk Exp$
*/
final class EXIFThumbnailReader extends ThumbnailReader {
    private final ImageReader reader;
    private final Directory ifd;
    private final ImageInputStream stream;
    private final int compression;

    private transient SoftReference<BufferedImage> cachedThumbnail;

    public EXIFThumbnailReader(ThumbnailReadProgressListener progressListener, ImageReader jpegReader, int imageIndex, int thumbnailIndex, Directory ifd, ImageInputStream stream) {
        super(progressListener, imageIndex, thumbnailIndex);
        this.reader = Validate.notNull(jpegReader);
        this.ifd = ifd;
        this.stream = stream;

        Entry compression = ifd.getEntryById(TIFF.TAG_COMPRESSION);

        this.compression = compression != null ? ((Number) compression.getValue()).intValue() : 6;
    }

    @Override
    public BufferedImage read() throws IOException {
        if (compression == 1) { // 1 = no compression
            processThumbnailStarted();
            BufferedImage thumbnail = readUncompressed();
            processThumbnailProgress(100f);
            processThumbnailComplete();

            return thumbnail;
        }
        else if (compression == 6) { // 6 = JPEG compression
            processThumbnailStarted();
            BufferedImage thumbnail = readJPEGCached(true);
            processThumbnailProgress(100f);
            processThumbnailComplete();

            return thumbnail;
        }
        else {
            throw new IIOException("Unsupported EXIF thumbnail compression: " + compression);
        }
    }

    private BufferedImage readJPEGCached(boolean pixelsExposed) throws IOException {
        BufferedImage thumbnail = cachedThumbnail != null ? cachedThumbnail.get() : null;

        if (thumbnail == null) {
            thumbnail = readJPEG();
        }

        cachedThumbnail = pixelsExposed ? null : new SoftReference<BufferedImage>(thumbnail);

        return thumbnail;
    }

    private BufferedImage readJPEG() throws IOException {
        // IFD1 should contain JPEG offset for JPEG thumbnail
        Entry jpegOffset = ifd.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);

        if (jpegOffset != null) {
            stream.seek(((Number) jpegOffset.getValue()).longValue());
            InputStream input = IIOUtil.createStreamAdapter(stream);

            // For certain EXIF files (encoded with TIFF.TAG_YCBCR_POSITIONING = 2?), we need
            // EXIF information to read the thumbnail correctly (otherwise the colors are messed up).
            // Probably related to: http://bugs.sun.com/view_bug.do?bug_id=4881314

            // HACK: Splice empty EXIF information into the thumbnail stream
            byte[] fakeEmptyExif = {
                    // SOI (from original data)
                    (byte) input.read(), (byte) input.read(),
                    // APP1 + len (016) + 'Exif' + 0-term + pad
                    (byte) 0xFF, (byte) 0xE1, 0, 16, 'E', 'x', 'i', 'f', 0, 0,
                    // Big-endian BOM (MM), TIFF magic (042), offset (0000)
                    'M', 'M', 0, 42, 0, 0, 0, 0,
            };

            input = new SequenceInputStream(new ByteArrayInputStream(fakeEmptyExif), input);

            try {
                MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(input);

                try {
                    return readJPEGThumbnail(reader, stream);
                }
                finally {
                    stream.close();
                }
            }
            finally {
                input.close();
            }
        }

        throw new IIOException("Missing JPEGInterchangeFormat tag for JPEG compressed EXIF thumbnail");
    }

    private BufferedImage readUncompressed() throws IOException {
        // Read ImageWidth, ImageLength (height) and BitsPerSample (=8 8 8, always)
        // PhotometricInterpretation (2=RGB, 6=YCbCr), SamplesPerPixel (=3, always),
        Entry width = ifd.getEntryById(TIFF.TAG_IMAGE_WIDTH);
        Entry height = ifd.getEntryById(TIFF.TAG_IMAGE_HEIGHT);

        if (width == null || height == null) {
            throw new IIOException("Missing dimensions for uncompressed EXIF thumbnail");
        }

        Entry bitsPerSample = ifd.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
        Entry samplesPerPixel = ifd.getEntryById(TIFF.TAG_SAMPLES_PER_PIXEL);
        Entry photometricInterpretation = ifd.getEntryById(TIFF.TAG_PHOTOMETRIC_INTERPRETATION);

        // Required
        int w = ((Number) width.getValue()).intValue();
        int h = ((Number) height.getValue()).intValue();

        if (bitsPerSample != null) {
            int[] bpp = (int[]) bitsPerSample.getValue();
            if (!Arrays.equals(bpp, new int[] {8, 8, 8})) {
                throw new IIOException("Unknown BitsPerSample value for uncompressed EXIF thumbnail (expected [8, 8, 8]): " + bitsPerSample.getValueAsString());
            }
        }

        if (samplesPerPixel != null && (Integer) samplesPerPixel.getValue() != 3) {
            throw new IIOException("Unknown SamplesPerPixel value for uncompressed EXIF thumbnail (expected 3): " + samplesPerPixel.getValueAsString());
        }

        int interpretation = photometricInterpretation != null ? ((Number) photometricInterpretation.getValue()).intValue() : 2;

        // IFD1 should contain strip offsets for uncompressed images
        Entry offset = ifd.getEntryById(TIFF.TAG_STRIP_OFFSETS);
        if (offset != null) {
            stream.seek(((Number) offset.getValue()).longValue());

            // Read raw image data, either RGB or YCbCr
            int thumbSize = w * h * 3;
            byte[] thumbData = JPEGImageReader.readFully(stream, thumbSize);

            switch (interpretation) {
                case 2:
                    // RGB
                    break;
                case 6:
                    // YCbCr
                    for (int i = 0, thumbDataLength = thumbData.length; i < thumbDataLength; i += 3) {
                        JPEGImageReader.YCbCrConverter.convertYCbCr2RGB(thumbData, thumbData, i);
                    }
                    break;
                default:
                    throw new IIOException("Unknown PhotometricInterpretation value for uncompressed EXIF thumbnail (expected 2 or 6): " + interpretation);
            }

            return ThumbnailReader.readRawThumbnail(thumbData, thumbData.length, 0, w, h);
        }

        throw new IIOException("Missing StripOffsets tag for uncompressed EXIF thumbnail");
    }

    @Override
    public int getWidth() throws IOException {
        if (compression == 1) { // 1 = no compression
            Entry width = ifd.getEntryById(TIFF.TAG_IMAGE_WIDTH);

            if (width == null) {
                throw new IIOException("Missing dimensions for uncompressed EXIF thumbnail");
            }

            return ((Number) width.getValue()).intValue();
        }
        else if (compression == 6) { // 6 = JPEG compression
            return readJPEGCached(false).getWidth();
        }
        else {
            throw new IIOException("Unsupported EXIF thumbnail compression (expected 1 or 6): " + compression);
        }
    }

    @Override
    public int getHeight() throws IOException {
        if (compression == 1) { // 1 = no compression
            Entry height = ifd.getEntryById(TIFF.TAG_IMAGE_HEIGHT);

            if (height == null) {
                throw new IIOException("Missing dimensions for uncompressed EXIF thumbnail");
            }

            return ((Number) height.getValue()).intValue();
        }
        else if (compression == 6) { // 6 = JPEG compression
            return readJPEGCached(false).getHeight();
        }
        else {
            throw new IIOException("Unsupported EXIF thumbnail compression  (expected 1 or 6): " + compression);
        }
    }
}
TOP

Related Classes of com.alkacon.simapi.CmykJpegReader.EXIFThumbnailReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.