| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.imaging.formats.tiff; |
| |
| import java.awt.Dimension; |
| import java.awt.Rectangle; |
| import java.awt.image.BufferedImage; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.PrintWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.commons.imaging.FormatCompliance; |
| import org.apache.commons.imaging.ImageFormat; |
| import org.apache.commons.imaging.ImageFormats; |
| import org.apache.commons.imaging.ImageInfo; |
| import org.apache.commons.imaging.ImageParser; |
| import org.apache.commons.imaging.ImageReadException; |
| import org.apache.commons.imaging.ImageWriteException; |
| import org.apache.commons.imaging.common.ByteOrder; |
| import org.apache.commons.imaging.common.IImageMetadata; |
| import org.apache.commons.imaging.common.ImageBuilder; |
| import org.apache.commons.imaging.common.bytesource.ByteSource; |
| import org.apache.commons.imaging.formats.tiff.TiffDirectory.ImageDataElement; |
| import org.apache.commons.imaging.formats.tiff.constants.TiffConstants; |
| import org.apache.commons.imaging.formats.tiff.constants.TiffEpTagConstants; |
| import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; |
| import org.apache.commons.imaging.formats.tiff.datareaders.DataReader; |
| import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreter; |
| import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterBiLevel; |
| import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterCieLab; |
| import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterCmyk; |
| import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterLogLuv; |
| import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterPalette; |
| import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterRgb; |
| import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterYCbCr; |
| import org.apache.commons.imaging.formats.tiff.write.TiffImageWriterLossy; |
| |
| public class TiffImageParser extends ImageParser implements TiffConstants { |
| public TiffImageParser() { |
| // setDebug(true); |
| } |
| |
| @Override |
| public String getName() { |
| return "Tiff-Custom"; |
| } |
| |
| @Override |
| public String getDefaultExtension() { |
| return DEFAULT_EXTENSION; |
| } |
| |
| private static final String DEFAULT_EXTENSION = ".tif"; |
| |
| private static final String ACCEPTED_EXTENSIONS[] = { ".tif", ".tiff", }; |
| |
| @Override |
| protected String[] getAcceptedExtensions() { |
| return ACCEPTED_EXTENSIONS; |
| } |
| |
| @Override |
| protected ImageFormat[] getAcceptedTypes() { |
| return new ImageFormat[] { ImageFormats.TIFF, // |
| }; |
| } |
| |
| @Override |
| public byte[] getICCProfileBytes(final ByteSource byteSource, final Map<String,Object> params) |
| throws ImageReadException, IOException { |
| final FormatCompliance formatCompliance = FormatCompliance.getDefault(); |
| final TiffContents contents = new TiffReader(isStrict(params)) |
| .readFirstDirectory(byteSource, params, false, formatCompliance); |
| final TiffDirectory directory = contents.directories.get(0); |
| |
| return directory.getFieldValue(TiffEpTagConstants.EXIF_TAG_INTER_COLOR_PROFILE, |
| false); |
| } |
| |
| @Override |
| public Dimension getImageSize(final ByteSource byteSource, final Map<String,Object> params) |
| throws ImageReadException, IOException { |
| final FormatCompliance formatCompliance = FormatCompliance.getDefault(); |
| final TiffContents contents = new TiffReader(isStrict(params)) |
| .readFirstDirectory(byteSource, params, false, formatCompliance); |
| final TiffDirectory directory = contents.directories.get(0); |
| |
| final TiffField widthField = directory.findField( |
| TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, true); |
| final TiffField heightField = directory.findField( |
| TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, true); |
| |
| if ((widthField == null) || (heightField == null)) { |
| throw new ImageReadException("TIFF image missing size info."); |
| } |
| |
| final int height = heightField.getIntValue(); |
| final int width = widthField.getIntValue(); |
| |
| return new Dimension(width, height); |
| } |
| |
| public byte[] embedICCProfile(final byte image[], final byte profile[]) { |
| return null; |
| } |
| |
| @Override |
| public boolean embedICCProfile(final File src, final File dst, final byte profile[]) { |
| return false; |
| } |
| |
| @Override |
| public IImageMetadata getMetadata(final ByteSource byteSource, final Map<String,Object> params) |
| throws ImageReadException, IOException { |
| final FormatCompliance formatCompliance = FormatCompliance.getDefault(); |
| final TiffReader tiffReader = new TiffReader(isStrict(params)); |
| final TiffContents contents = tiffReader.readContents(byteSource, params, |
| formatCompliance); |
| |
| final List<TiffDirectory> directories = contents.directories; |
| |
| final TiffImageMetadata result = new TiffImageMetadata(contents); |
| |
| for (int i = 0; i < directories.size(); i++) { |
| final TiffDirectory dir = directories.get(i); |
| |
| final TiffImageMetadata.Directory metadataDirectory = new TiffImageMetadata.Directory( |
| tiffReader.getByteOrder(), dir); |
| |
| final List<TiffField> entries = dir.getDirectoryEntries(); |
| |
| for (int j = 0; j < entries.size(); j++) { |
| final TiffField entry = entries.get(j); |
| metadataDirectory.add(entry); |
| } |
| |
| result.add(metadataDirectory); |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public ImageInfo getImageInfo(final ByteSource byteSource, final Map<String,Object> params) |
| throws ImageReadException, IOException { |
| final FormatCompliance formatCompliance = FormatCompliance.getDefault(); |
| final TiffContents contents = new TiffReader(isStrict(params)) |
| .readDirectories(byteSource, false, formatCompliance); |
| final TiffDirectory directory = contents.directories.get(0); |
| |
| final TiffField widthField = directory.findField( |
| TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, true); |
| final TiffField heightField = directory.findField( |
| TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, true); |
| |
| if ((widthField == null) || (heightField == null)) { |
| throw new ImageReadException("TIFF image missing size info."); |
| } |
| |
| final int height = heightField.getIntValue(); |
| final int width = widthField.getIntValue(); |
| |
| // ------------------- |
| |
| final TiffField resolutionUnitField = directory |
| .findField(TiffTagConstants.TIFF_TAG_RESOLUTION_UNIT); |
| int resolutionUnit = 2; // Inch |
| if ((resolutionUnitField != null) |
| && (resolutionUnitField.getValue() != null)) { |
| resolutionUnit = resolutionUnitField.getIntValue(); |
| } |
| |
| double unitsPerInch = -1; |
| switch (resolutionUnit) { |
| case 1: |
| break; |
| case 2: // Inch |
| unitsPerInch = 1.0; |
| break; |
| case 3: // Centimeter |
| unitsPerInch = 2.54; |
| break; |
| default: |
| break; |
| |
| } |
| final TiffField xResolutionField = directory |
| .findField(TiffTagConstants.TIFF_TAG_XRESOLUTION); |
| final TiffField yResolutionField = directory |
| .findField(TiffTagConstants.TIFF_TAG_YRESOLUTION); |
| |
| int physicalWidthDpi = -1; |
| float physicalWidthInch = -1; |
| int physicalHeightDpi = -1; |
| float physicalHeightInch = -1; |
| |
| if (unitsPerInch > 0) { |
| if ((xResolutionField != null) |
| && (xResolutionField.getValue() != null)) { |
| final double XResolutionPixelsPerUnit = xResolutionField |
| .getDoubleValue(); |
| physicalWidthDpi = (int) Math |
| .round((XResolutionPixelsPerUnit * unitsPerInch)); |
| physicalWidthInch = (float) (width / (XResolutionPixelsPerUnit * unitsPerInch)); |
| } |
| if ((yResolutionField != null) |
| && (yResolutionField.getValue() != null)) { |
| final double YResolutionPixelsPerUnit = yResolutionField |
| .getDoubleValue(); |
| physicalHeightDpi = (int) Math |
| .round((YResolutionPixelsPerUnit * unitsPerInch)); |
| physicalHeightInch = (float) (height / (YResolutionPixelsPerUnit * unitsPerInch)); |
| } |
| } |
| |
| // ------------------- |
| |
| final TiffField bitsPerSampleField = directory |
| .findField(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE); |
| |
| int bitsPerSample = 1; |
| if ((bitsPerSampleField != null) |
| && (bitsPerSampleField.getValue() != null)) { |
| bitsPerSample = bitsPerSampleField.getIntValueOrArraySum(); |
| } |
| |
| final int bitsPerPixel = bitsPerSample; // assume grayscale; |
| // dunno if this handles colormapped images correctly. |
| |
| // ------------------- |
| |
| final List<String> comments = new ArrayList<String>(); |
| final List<TiffField> entries = directory.entries; |
| for (int i = 0; i < entries.size(); i++) { |
| final TiffField field = entries.get(i); |
| final String comment = field.toString(); |
| comments.add(comment); |
| } |
| |
| final ImageFormat format = ImageFormats.TIFF; |
| final String formatName = "TIFF Tag-based Image File Format"; |
| final String mimeType = "image/tiff"; |
| final int numberOfImages = contents.directories.size(); |
| // not accurate ... only reflects first |
| final boolean isProgressive = false; |
| // is TIFF ever interlaced/progressive? |
| |
| final String formatDetails = "Tiff v." + contents.header.tiffVersion; |
| |
| final boolean isTransparent = false; // TODO: wrong |
| boolean usesPalette = false; |
| final TiffField colorMapField = directory |
| .findField(TiffTagConstants.TIFF_TAG_COLOR_MAP); |
| if (colorMapField != null) { |
| usesPalette = true; |
| } |
| |
| final int colorType = ImageInfo.COLOR_TYPE_RGB; |
| |
| final int compression = 0xffff & directory |
| .getSingleFieldValue(TiffTagConstants.TIFF_TAG_COMPRESSION); |
| String compressionAlgorithm; |
| |
| switch (compression) { |
| case TIFF_COMPRESSION_UNCOMPRESSED_1: |
| compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_NONE; |
| break; |
| case TIFF_COMPRESSION_CCITT_1D: |
| compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_CCITT_1D; |
| break; |
| case TIFF_COMPRESSION_CCITT_GROUP_3: |
| compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_CCITT_GROUP_3; |
| break; |
| case TIFF_COMPRESSION_CCITT_GROUP_4: |
| compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_CCITT_GROUP_4; |
| break; |
| case TIFF_COMPRESSION_LZW: |
| compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_LZW; |
| break; |
| case TIFF_COMPRESSION_JPEG: |
| compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_JPEG; |
| break; |
| case TIFF_COMPRESSION_UNCOMPRESSED_2: |
| compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_NONE; |
| break; |
| case TIFF_COMPRESSION_PACKBITS: |
| compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_PACKBITS; |
| break; |
| default: |
| compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_UNKNOWN; |
| break; |
| } |
| |
| final ImageInfo result = new ImageInfo(formatDetails, bitsPerPixel, comments, |
| format, formatName, height, mimeType, numberOfImages, |
| physicalHeightDpi, physicalHeightInch, physicalWidthDpi, |
| physicalWidthInch, width, isProgressive, isTransparent, |
| usesPalette, colorType, compressionAlgorithm); |
| |
| return result; |
| } |
| |
| @Override |
| public String getXmpXml(final ByteSource byteSource, final Map<String,Object> params) |
| throws ImageReadException, IOException { |
| final FormatCompliance formatCompliance = FormatCompliance.getDefault(); |
| final TiffContents contents = new TiffReader(isStrict(params)) |
| .readDirectories(byteSource, false, formatCompliance); |
| final TiffDirectory directory = contents.directories.get(0); |
| |
| final byte bytes[] = directory.getFieldValue(TiffTagConstants.TIFF_TAG_XMP, |
| false); |
| if (bytes == null) { |
| return null; |
| } |
| |
| try { |
| // segment data is UTF-8 encoded xml. |
| final String xml = new String(bytes, "utf-8"); |
| return xml; |
| } catch (final UnsupportedEncodingException e) { |
| throw new ImageReadException("Invalid JPEG XMP Segment."); |
| } |
| } |
| |
| @Override |
| public boolean dumpImageFile(final PrintWriter pw, final ByteSource byteSource) |
| throws ImageReadException, IOException { |
| try { |
| pw.println("tiff.dumpImageFile"); |
| |
| { |
| final ImageInfo imageData = getImageInfo(byteSource); |
| if (imageData == null) { |
| return false; |
| } |
| |
| imageData.toString(pw, ""); |
| } |
| |
| pw.println(""); |
| |
| // try |
| { |
| final FormatCompliance formatCompliance = FormatCompliance |
| .getDefault(); |
| final Map<String,Object> params = null; |
| final TiffContents contents = new TiffReader(true).readContents( |
| byteSource, params, formatCompliance); |
| |
| final List<TiffDirectory> directories = contents.directories; |
| |
| if (directories == null) { |
| return false; |
| } |
| |
| for (int d = 0; d < directories.size(); d++) { |
| final TiffDirectory directory = directories.get(d); |
| |
| final List<TiffField> entries = directory.entries; |
| |
| if (entries == null) { |
| return false; |
| } |
| |
| // Debug.debug("directory offset", directory.offset); |
| |
| for (int i = 0; i < entries.size(); i++) { |
| final TiffField field = entries.get(i); |
| |
| field.dump(pw, d + ""); |
| } |
| } |
| |
| pw.println(""); |
| } |
| // catch (Exception e) |
| // { |
| // Debug.debug(e); |
| // pw.println(""); |
| // return false; |
| // } |
| |
| return true; |
| } finally { |
| pw.println(""); |
| } |
| } |
| |
| @Override |
| public FormatCompliance getFormatCompliance(final ByteSource byteSource) |
| throws ImageReadException, IOException { |
| final FormatCompliance formatCompliance = FormatCompliance.getDefault(); |
| final Map<String,Object> params = null; |
| new TiffReader(isStrict(params)).readContents(byteSource, params, |
| formatCompliance); |
| return formatCompliance; |
| } |
| |
| public List<byte[]> collectRawImageData(final ByteSource byteSource, final Map<String,Object> params) |
| throws ImageReadException, IOException { |
| final FormatCompliance formatCompliance = FormatCompliance.getDefault(); |
| final TiffContents contents = new TiffReader(isStrict(params)) |
| .readDirectories(byteSource, true, formatCompliance); |
| |
| final List<byte[]> result = new ArrayList<byte[]>(); |
| for (int i = 0; i < contents.directories.size(); i++) { |
| final TiffDirectory directory = contents.directories.get(i); |
| final List<ImageDataElement> dataElements = directory |
| .getTiffRawImageDataElements(); |
| for (int j = 0; j < dataElements.size(); j++) { |
| final TiffDirectory.ImageDataElement element = dataElements.get(j); |
| final byte bytes[] = byteSource.getBlock(element.offset, |
| element.length); |
| result.add(bytes); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Gets a buffered image specified by the byte source. |
| * The TiffImageParser class features support for a number of options that |
| * are unique to the TIFF format. These options can be specified by |
| * supplying the appropriate parameters using the keys from the |
| * TiffConstants class and the params argument for this method. |
| * <h4>Loading Partial Images</h4> |
| * The TIFF parser includes support for loading partial images without |
| * committing significantly more memory resources than are necessary |
| * to store the image. This feature is useful for conserving memory |
| * in applications that require a relatively small sub image from a |
| * very large TIFF file. The specifications for partial images are |
| * as follows: |
| * <code><pre> |
| * HashMap<String, Object> params = new HashMap<String, Object>(); |
| * params.put(TiffConstants.PARAM_KEY_SUBIMAGE_X, new Integer(x)); |
| * params.put(TiffConstants.PARAM_KEY_SUBIMAGE_Y, new Integer(y)); |
| * params.put(TiffConstants.PARAM_KEY_SUBIMAGE_WIDTH, new Integer(width)); |
| * params.put(TiffConstants.PARAM_KEY_SUBIMAGE_HEIGHT, new Integer(height)); |
| * </pre></code> |
| * Note that the arguments x, y, width, and height must specify a |
| * valid rectangular region that is fully contained within the |
| * source TIFF image. |
| * @param byteSource A valid instance of ByteSource |
| * @param params Optional instructions for special-handling or |
| * interpretation of the input data (null objects are permitted and |
| * must be supported by implementations). |
| * @return A valid instance of BufferedImage. |
| * @throws ImageReadException In the event that the the specified |
| * content does not conform to the format of the specific parser |
| * implementation. |
| * @throws IOException In the event of unsuccessful read or |
| * access operation. |
| */ |
| @Override |
| public BufferedImage getBufferedImage(final ByteSource byteSource, final Map<String,Object> params) |
| throws ImageReadException, IOException { |
| final FormatCompliance formatCompliance = FormatCompliance.getDefault(); |
| final TiffReader reader = new TiffReader(isStrict(params)); |
| final TiffContents contents = reader.readFirstDirectory(byteSource, params, |
| true, formatCompliance); |
| final ByteOrder byteOrder = reader.getByteOrder(); |
| final TiffDirectory directory = contents.directories.get(0); |
| final BufferedImage result = directory.getTiffImage(byteOrder, params); |
| if (null == result) { |
| throw new ImageReadException("TIFF does not contain an image."); |
| } |
| return result; |
| } |
| |
| @Override |
| public List<BufferedImage> getAllBufferedImages(final ByteSource byteSource) |
| throws ImageReadException, IOException { |
| final FormatCompliance formatCompliance = FormatCompliance.getDefault(); |
| final TiffReader tiffReader = new TiffReader(true); |
| final TiffContents contents = tiffReader.readDirectories(byteSource, true, |
| formatCompliance); |
| final List<BufferedImage> results = new ArrayList<BufferedImage>(); |
| for (int i = 0; i < contents.directories.size(); i++) { |
| final TiffDirectory directory = contents.directories.get(i); |
| final BufferedImage result = directory.getTiffImage( |
| tiffReader.getByteOrder(), null); |
| if (result != null) { |
| results.add(result); |
| } |
| } |
| return results; |
| } |
| |
| private Integer getIntegerParameter( |
| final String key, final Map<String, Object>params) |
| throws ImageReadException |
| { |
| if (params == null) { |
| return null; |
| } |
| |
| if(!params.containsKey(key)) { |
| return null; |
| } |
| |
| final Object obj = params.get(key); |
| |
| if(obj instanceof Integer){ |
| return (Integer)obj; |
| } |
| throw new ImageReadException( |
| "Non-Integer parameter "+key); |
| } |
| |
| private Rectangle checkForSubImage( |
| final Map<String, Object> params) |
| throws ImageReadException |
| { |
| Integer ix0, iy0, iwidth, iheight; |
| ix0 = getIntegerParameter( |
| TiffConstants.PARAM_KEY_SUBIMAGE_X, params); |
| iy0 = getIntegerParameter( |
| TiffConstants.PARAM_KEY_SUBIMAGE_Y, params); |
| iwidth = getIntegerParameter( |
| TiffConstants.PARAM_KEY_SUBIMAGE_WIDTH, params); |
| iheight = getIntegerParameter( |
| TiffConstants.PARAM_KEY_SUBIMAGE_HEIGHT, params); |
| if (ix0 == null && iy0 == null && iwidth == null && iheight == null) { |
| return null; |
| } |
| |
| final StringBuilder sb = new StringBuilder(); |
| if (ix0 == null) { |
| sb.append(" x0,"); |
| } |
| if (iy0 == null) { |
| sb.append(" y0,"); |
| } |
| if (iwidth == null) { |
| sb.append(" width,"); |
| } |
| if (iheight == null) { |
| sb.append(" height,"); |
| } |
| if (sb.length() > 0) { |
| sb.setLength(sb.length() - 1); |
| throw new ImageReadException( |
| "Incomplete subimage parameters, missing" |
| + sb.toString()); |
| } |
| |
| final int x0 = ix0.intValue(); |
| final int y0 = iy0.intValue(); |
| final int width = iwidth.intValue(); |
| final int height = iheight.intValue(); |
| |
| return new Rectangle(x0, y0, width, height); |
| } |
| |
| protected BufferedImage getBufferedImage(final TiffDirectory directory, |
| final ByteOrder byteOrder, final Map<String,Object> params) |
| throws ImageReadException, IOException |
| { |
| final List<TiffField> entries = directory.entries; |
| |
| if (entries == null) { |
| throw new ImageReadException("TIFF missing entries"); |
| } |
| |
| final int photometricInterpretation = 0xffff & directory.getSingleFieldValue( |
| TiffTagConstants.TIFF_TAG_PHOTOMETRIC_INTERPRETATION); |
| final int compression = 0xffff & directory.getSingleFieldValue( |
| TiffTagConstants.TIFF_TAG_COMPRESSION); |
| final int width = directory.getSingleFieldValue( |
| TiffTagConstants.TIFF_TAG_IMAGE_WIDTH); |
| final int height = directory.getSingleFieldValue( |
| TiffTagConstants.TIFF_TAG_IMAGE_LENGTH); |
| Rectangle subImage = checkForSubImage(params); |
| if(subImage!=null){ |
| // Check for valid subimage specification. The following checks |
| // are consistent with BufferedImage.getSubimage() |
| if (subImage.width <= 0) { |
| throw new ImageReadException("negative or zero subimage width"); |
| } |
| if (subImage.height <= 0) { |
| throw new ImageReadException("negative or zero subimage height"); |
| } |
| if(subImage.x<0 || subImage.x>=width){ |
| throw new ImageReadException("subimage x is outside raster"); |
| } |
| if(subImage.x+subImage.width>width){ |
| throw new ImageReadException( |
| "subimage (x+width) is outside raster"); |
| } |
| if(subImage.y<0 || subImage.y>=height){ |
| throw new ImageReadException("subimage y is outside raster"); |
| } |
| if(subImage.y+subImage.height>height){ |
| throw new ImageReadException( |
| "subimage (y+height) is outside raster"); |
| } |
| |
| // if the subimage is just the same thing as the whole |
| // image, suppress the subimage processing |
| if(subImage.x==0 |
| && subImage.y==0 |
| && subImage.width==width |
| && subImage.height==height){ |
| subImage = null; |
| } |
| } |
| |
| |
| int samplesPerPixel = 1; |
| final TiffField samplesPerPixelField = directory.findField( |
| TiffTagConstants.TIFF_TAG_SAMPLES_PER_PIXEL); |
| if (samplesPerPixelField != null) { |
| samplesPerPixel = samplesPerPixelField.getIntValue(); |
| } |
| int bitsPerSample[] = { 1 }; |
| int bitsPerPixel = samplesPerPixel; |
| final TiffField bitsPerSampleField = directory.findField( |
| TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE); |
| if (bitsPerSampleField != null) { |
| bitsPerSample = bitsPerSampleField.getIntArrayValue(); |
| bitsPerPixel = bitsPerSampleField.getIntValueOrArraySum(); |
| } |
| |
| // int bitsPerPixel = getTagAsValueOrArraySum(entries, |
| // TIFF_TAG_BITS_PER_SAMPLE); |
| |
| int predictor = -1; |
| { |
| // dumpOptionalNumberTag(entries, TIFF_TAG_FILL_ORDER); |
| // dumpOptionalNumberTag(entries, TIFF_TAG_FREE_BYTE_COUNTS); |
| // dumpOptionalNumberTag(entries, TIFF_TAG_FREE_OFFSETS); |
| // dumpOptionalNumberTag(entries, TIFF_TAG_ORIENTATION); |
| // dumpOptionalNumberTag(entries, TIFF_TAG_PLANAR_CONFIGURATION); |
| final TiffField predictorField = directory.findField( |
| TiffTagConstants.TIFF_TAG_PREDICTOR); |
| if (null != predictorField) { |
| predictor = predictorField.getIntValueOrArraySum(); |
| } |
| } |
| |
| if (samplesPerPixel != bitsPerSample.length) { |
| throw new ImageReadException("Tiff: samplesPerPixel (" |
| + samplesPerPixel + ")!=fBitsPerSample.length (" |
| + bitsPerSample.length + ")"); |
| } |
| |
| |
| |
| final PhotometricInterpreter photometricInterpreter = getPhotometricInterpreter( |
| directory, photometricInterpretation, bitsPerPixel, |
| bitsPerSample, predictor, samplesPerPixel, width, height); |
| |
| final TiffImageData imageData = directory.getTiffImageData(); |
| |
| final DataReader dataReader = imageData.getDataReader(directory, |
| photometricInterpreter, bitsPerPixel, bitsPerSample, predictor, |
| samplesPerPixel, width, height, compression, byteOrder); |
| |
| BufferedImage result = null; |
| if (subImage != null) { |
| result = dataReader.readImageData(subImage); |
| } else { |
| final boolean hasAlpha = false; |
| final ImageBuilder imageBuilder = new ImageBuilder(width, height, hasAlpha); |
| |
| dataReader.readImageData(imageBuilder); |
| result = imageBuilder.getBufferedImage(); |
| } |
| photometricInterpreter.dumpstats(); |
| return result; |
| } |
| |
| private PhotometricInterpreter getPhotometricInterpreter( |
| final TiffDirectory directory, final int photometricInterpretation, |
| final int bitsPerPixel, final int bitsPerSample[], final int predictor, |
| final int samplesPerPixel, final int width, final int height) |
| throws ImageReadException { |
| switch (photometricInterpretation) { |
| case 0: |
| case 1: |
| final boolean invert = photometricInterpretation == 0; |
| |
| return new PhotometricInterpreterBiLevel(bitsPerPixel, |
| samplesPerPixel, bitsPerSample, predictor, width, height, |
| invert); |
| case 3: // Palette |
| { |
| final int colorMap[] = directory.findField( |
| TiffTagConstants.TIFF_TAG_COLOR_MAP, true) |
| .getIntArrayValue(); |
| |
| final int expected_colormap_size = 3 * (1 << bitsPerPixel); |
| |
| if (colorMap.length != expected_colormap_size) { |
| throw new ImageReadException("Tiff: fColorMap.length (" |
| + colorMap.length + ")!=expected_colormap_size (" |
| + expected_colormap_size + ")"); |
| } |
| |
| return new PhotometricInterpreterPalette(samplesPerPixel, |
| bitsPerSample, predictor, width, height, colorMap); |
| } |
| case 2: // RGB |
| return new PhotometricInterpreterRgb(samplesPerPixel, |
| bitsPerSample, predictor, width, height); |
| case 5: // CMYK |
| return new PhotometricInterpreterCmyk(samplesPerPixel, |
| bitsPerSample, predictor, width, height); |
| case 6: // |
| { |
| final double yCbCrCoefficients[] = directory.findField( |
| TiffTagConstants.TIFF_TAG_YCBCR_COEFFICIENTS, true) |
| .getDoubleArrayValue(); |
| |
| final int yCbCrPositioning[] = directory.findField( |
| TiffTagConstants.TIFF_TAG_YCBCR_POSITIONING, true) |
| .getIntArrayValue(); |
| final int yCbCrSubSampling[] = directory.findField( |
| TiffTagConstants.TIFF_TAG_YCBCR_SUB_SAMPLING, true) |
| .getIntArrayValue(); |
| |
| final double referenceBlackWhite[] = directory.findField( |
| TiffTagConstants.TIFF_TAG_REFERENCE_BLACK_WHITE, true) |
| .getDoubleArrayValue(); |
| |
| return new PhotometricInterpreterYCbCr(yCbCrCoefficients, |
| yCbCrPositioning, yCbCrSubSampling, referenceBlackWhite, |
| samplesPerPixel, bitsPerSample, predictor, width, height); |
| } |
| |
| case 8: |
| return new PhotometricInterpreterCieLab(samplesPerPixel, |
| bitsPerSample, predictor, width, height); |
| |
| case 32844: |
| case 32845: { |
| final boolean yonly = (photometricInterpretation == 32844); |
| return new PhotometricInterpreterLogLuv(samplesPerPixel, |
| bitsPerSample, predictor, width, height, yonly); |
| } |
| |
| default: |
| throw new ImageReadException( |
| "TIFF: Unknown fPhotometricInterpretation: " |
| + photometricInterpretation); |
| } |
| } |
| |
| @Override |
| public void writeImage(final BufferedImage src, final OutputStream os, final Map<String,Object> params) |
| throws ImageWriteException, IOException { |
| new TiffImageWriterLossy().writeImage(src, os, params); |
| } |
| |
| } |