| /* |
| * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package com.sun.imageio.plugins.tiff; |
| |
| import javax.imageio.plugins.tiff.BaselineTIFFTagSet; |
| import javax.imageio.plugins.tiff.TIFFField; |
| import javax.imageio.plugins.tiff.TIFFTag; |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.util.Iterator; |
| import javax.imageio.ImageReader; |
| import javax.imageio.ImageWriteParam; |
| import javax.imageio.metadata.IIOMetadata; |
| import javax.imageio.spi.IIORegistry; |
| import javax.imageio.spi.ImageReaderSpi; |
| import javax.imageio.spi.ServiceRegistry; |
| import javax.imageio.stream.MemoryCacheImageInputStream; |
| import javax.imageio.stream.MemoryCacheImageOutputStream; |
| |
| /** |
| * Compressor for encoding compression type 7, TTN2/Adobe JPEG-in-TIFF. |
| */ |
| public class TIFFJPEGCompressor extends TIFFBaseJPEGCompressor { |
| |
| // Subsampling factor for chroma bands (Cb Cr). |
| static final int CHROMA_SUBSAMPLING = 2; |
| |
| /** |
| * A filter which identifies the ImageReaderSpi of a JPEG reader |
| * which supports JPEG native stream metadata. |
| */ |
| private static class JPEGSPIFilter implements ServiceRegistry.Filter { |
| JPEGSPIFilter() {} |
| |
| public boolean filter(Object provider) { |
| ImageReaderSpi readerSPI = (ImageReaderSpi)provider; |
| |
| if(readerSPI != null) { |
| String streamMetadataName = |
| readerSPI.getNativeStreamMetadataFormatName(); |
| if(streamMetadataName != null) { |
| return streamMetadataName.equals(STREAM_METADATA_NAME); |
| } else { |
| return false; |
| } |
| } |
| |
| return false; |
| } |
| } |
| |
| /** |
| * Retrieves a JPEG reader which supports native JPEG stream metadata. |
| */ |
| private static ImageReader getJPEGTablesReader() { |
| ImageReader jpegReader = null; |
| |
| try { |
| IIORegistry registry = IIORegistry.getDefaultInstance(); |
| Iterator<?> readerSPIs = |
| registry.getServiceProviders(ImageReaderSpi.class, |
| new JPEGSPIFilter(), |
| true); |
| if(readerSPIs.hasNext()) { |
| ImageReaderSpi jpegReaderSPI = |
| (ImageReaderSpi)readerSPIs.next(); |
| jpegReader = jpegReaderSPI.createReaderInstance(); |
| } |
| } catch(Exception e) { |
| // Ignore it ... |
| } |
| |
| return jpegReader; |
| } |
| |
| public TIFFJPEGCompressor(ImageWriteParam param) { |
| super("JPEG", BaselineTIFFTagSet.COMPRESSION_JPEG, false, param); |
| } |
| |
| /** |
| * Sets the value of the {@code metadata} field. |
| * |
| * <p>The implementation in this class also adds the TIFF fields |
| * JPEGTables, YCbCrSubSampling, YCbCrPositioning, and |
| * ReferenceBlackWhite superseding any prior settings of those |
| * fields.</p> |
| * |
| * @param metadata the {@code IIOMetadata} object for the |
| * image being written. |
| * |
| * @see #getMetadata() |
| */ |
| public void setMetadata(IIOMetadata metadata) { |
| super.setMetadata(metadata); |
| |
| if (metadata instanceof TIFFImageMetadata) { |
| TIFFImageMetadata tim = (TIFFImageMetadata)metadata; |
| TIFFIFD rootIFD = tim.getRootIFD(); |
| BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance(); |
| |
| TIFFField f = |
| tim.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL); |
| int numBands = f.getAsInt(0); |
| |
| if(numBands == 1) { |
| // Remove YCbCr fields not relevant for grayscale. |
| |
| rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING); |
| rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING); |
| rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE); |
| } else { // numBands == 3 |
| // Replace YCbCr fields. |
| |
| // YCbCrSubSampling |
| TIFFField YCbCrSubSamplingField = new TIFFField |
| (base.getTag(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING), |
| TIFFTag.TIFF_SHORT, 2, |
| new char[] {CHROMA_SUBSAMPLING, CHROMA_SUBSAMPLING}); |
| rootIFD.addTIFFField(YCbCrSubSamplingField); |
| |
| // YCbCrPositioning |
| TIFFField YCbCrPositioningField = new TIFFField |
| (base.getTag(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING), |
| TIFFTag.TIFF_SHORT, 1, |
| new char[] |
| {BaselineTIFFTagSet.Y_CB_CR_POSITIONING_CENTERED}); |
| rootIFD.addTIFFField(YCbCrPositioningField); |
| |
| // ReferenceBlackWhite |
| TIFFField referenceBlackWhiteField = new TIFFField |
| (base.getTag(BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE), |
| TIFFTag.TIFF_RATIONAL, 6, |
| new long[][] { // no headroon/footroom |
| {0, 1}, {255, 1}, |
| {128, 1}, {255, 1}, |
| {128, 1}, {255, 1} |
| }); |
| rootIFD.addTIFFField(referenceBlackWhiteField); |
| } |
| |
| // JPEGTables field is written if and only if one is |
| // already present in the metadata. If one is present |
| // and has either zero length or does not represent a |
| // valid tables-only stream, then a JPEGTables field |
| // will be written initialized to the standard tables- |
| // only stream written by the JPEG writer. |
| |
| // Retrieve the JPEGTables field. |
| TIFFField JPEGTablesField = |
| tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_TABLES); |
| |
| // Initialize JPEG writer to one supporting abbreviated streams. |
| if(JPEGTablesField != null) { |
| // Intialize the JPEG writer to one that supports stream |
| // metadata, i.e., abbreviated streams, and may or may not |
| // support image metadata. |
| initJPEGWriter(true, false); |
| } |
| |
| // Write JPEGTables field if a writer supporting abbreviated |
| // streams was available. |
| if(JPEGTablesField != null && JPEGWriter != null) { |
| // Set the abbreviated stream flag. |
| this.writeAbbreviatedStream = true; |
| |
| //Branch based on field value count. |
| if(JPEGTablesField.getCount() > 0) { |
| // Derive the stream metadata from the field. |
| |
| // Get the field values. |
| byte[] tables = JPEGTablesField.getAsBytes(); |
| |
| // Create an input stream for the tables. |
| ByteArrayInputStream bais = |
| new ByteArrayInputStream(tables); |
| MemoryCacheImageInputStream iis = |
| new MemoryCacheImageInputStream(bais); |
| |
| // Read the tables stream using the JPEG reader. |
| ImageReader jpegReader = getJPEGTablesReader(); |
| jpegReader.setInput(iis); |
| |
| // Initialize the stream metadata object. |
| try { |
| JPEGStreamMetadata = jpegReader.getStreamMetadata(); |
| } catch(Exception e) { |
| // Fall back to default tables. |
| JPEGStreamMetadata = null; |
| } finally { |
| jpegReader.reset(); |
| } |
| } |
| |
| if(JPEGStreamMetadata == null) { |
| // Derive the field from default stream metadata. |
| |
| // Get default stream metadata. |
| JPEGStreamMetadata = |
| JPEGWriter.getDefaultStreamMetadata(JPEGParam); |
| |
| // Create an output stream for the tables. |
| ByteArrayOutputStream tableByteStream = |
| new ByteArrayOutputStream(); |
| MemoryCacheImageOutputStream tableStream = |
| new MemoryCacheImageOutputStream(tableByteStream); |
| |
| // Write a tables-only stream. |
| JPEGWriter.setOutput(tableStream); |
| try { |
| JPEGWriter.prepareWriteSequence(JPEGStreamMetadata); |
| tableStream.flush(); |
| JPEGWriter.endWriteSequence(); |
| |
| // Get the tables-only stream content. |
| byte[] tables = tableByteStream.toByteArray(); |
| |
| // Add the JPEGTables field. |
| JPEGTablesField = new TIFFField |
| (base.getTag(BaselineTIFFTagSet.TAG_JPEG_TABLES), |
| TIFFTag.TIFF_UNDEFINED, |
| tables.length, |
| tables); |
| rootIFD.addTIFFField(JPEGTablesField); |
| } catch(Exception e) { |
| // Do not write JPEGTables field. |
| rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_TABLES); |
| this.writeAbbreviatedStream = false; |
| } |
| } |
| } else { // Do not write JPEGTables field. |
| // Remove any field present. |
| rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_TABLES); |
| |
| // Initialize the writer preferring codecLib. |
| initJPEGWriter(false, false); |
| } |
| } |
| } |
| } |