From ad0d265f4f93a555943403774f3ead40a49aedbf Mon Sep 17 00:00:00 2001 From: Schmidor Date: Wed, 2 Dec 2015 01:48:38 +0100 Subject: [PATCH] Init TIFFUtilities for mereging, splitting and rotating tiff pages --- .../contrib/tiff/TIFFUtilities.java | 415 ++++++++++++++++++ .../contrib/tiff/TIFFUtilitiesTest.java | 204 +++++++++ .../test/resources/contrib/tiff/multipage.tif | Bin 0 -> 824 bytes .../imageio/plugins/tiff/TIFFBaseline.java | 2 +- .../imageio/plugins/tiff/TIFFExtension.java | 2 +- .../imageio/plugins/tiff/TIFFImageWriter.java | 4 +- 6 files changed, 623 insertions(+), 4 deletions(-) create mode 100644 contrib/src/main/java/com/twelvemonkeys/contrib/tiff/TIFFUtilities.java create mode 100644 contrib/src/test/java/com/twelvemonkeys/contrib/tiff/TIFFUtilitiesTest.java create mode 100644 contrib/src/test/resources/contrib/tiff/multipage.tif diff --git a/contrib/src/main/java/com/twelvemonkeys/contrib/tiff/TIFFUtilities.java b/contrib/src/main/java/com/twelvemonkeys/contrib/tiff/TIFFUtilities.java new file mode 100644 index 00000000..d7e63381 --- /dev/null +++ b/contrib/src/main/java/com/twelvemonkeys/contrib/tiff/TIFFUtilities.java @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2013, 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.twelvemonkeys.contrib.tiff; + +import com.twelvemonkeys.imageio.metadata.AbstractDirectory; +import com.twelvemonkeys.imageio.metadata.CompoundDirectory; +import com.twelvemonkeys.imageio.metadata.Directory; +import com.twelvemonkeys.imageio.metadata.Entry; +import com.twelvemonkeys.imageio.metadata.exif.EXIFReader; +import com.twelvemonkeys.imageio.metadata.exif.EXIFWriter; +import com.twelvemonkeys.imageio.metadata.exif.TIFF; +import com.twelvemonkeys.imageio.plugins.tiff.TIFFBaseline; +import com.twelvemonkeys.imageio.plugins.tiff.TIFFExtension; +import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriter; +import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriter.TIFFEntry; +import com.twelvemonkeys.lang.Validate; + +import javax.imageio.ImageIO; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.ImageOutputStream; +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * TIFFUtilities for manipulation TIFF Images and Metadata + * + * @author Oliver Schmidtmer + * @author last modified by $Author$ + * @version $Id$ + */ +public class TIFFUtilities { + private TIFFUtilities() { + } + + ; + + /** + * Merges all pages from the input TIFF files into one TIFF file at the + * output location. + * + * @param inputFiles + * @param outputFile + * @throws IOException + */ + public static void merge(List inputFiles, File outputFile) throws IOException { + ImageOutputStream output = null; + try { + output = ImageIO.createImageOutputStream(outputFile); + + for (File file : inputFiles) { + ImageInputStream input = null; + try { + input = ImageIO.createImageInputStream(file); + List pages = getPages(input); + writePages(output, pages); + } + finally { + if (input != null) { + input.close(); + } + } + } + } + finally { + if (output != null) { + output.flush(); + output.close(); + } + } + } + + /** + * Splits all pages from the input TIFF file to one file per page in the + * output directory. + * + * @param inputFile + * @param outputDirectory + * @return generated files + * @throws IOException + */ + public static List split(File inputFile, File outputDirectory) throws IOException { + ImageInputStream input = null; + List outputFiles = new ArrayList<>(); + try { + input = ImageIO.createImageInputStream(inputFile); + List pages = getPages(input); + int pageNo = 1; + for (TIFFPage tiffPage : pages) { + ArrayList outputPages = new ArrayList(1); + ImageOutputStream outputStream = null; + try { + File outputFile = new File(outputDirectory, String.format("%04d", pageNo) + ".tif"); + outputStream = ImageIO.createImageOutputStream(outputFile); + outputPages.clear(); + outputPages.add(tiffPage); + writePages(outputStream, outputPages); + outputFiles.add(outputFile); + } + finally { + if (outputStream != null) { + outputStream.flush(); + outputStream.close(); + } + } + ++pageNo; + } + } + finally { + if (input != null) { + input.close(); + } + } + return outputFiles; + } + + /** + * Rotates all pages of a TIFF file by changing TIFF.TAG_ORIENTATION. + *

+ * NOTICE: TIFF.TAG_ORIENTATION is an advice how the image is meant do be + * displayed. Other metadata, such as width and height, relate to the image + * as how it is stored. The ImageIO TIFF plugin does not handle orientation. + * Use {@link TIFFUtilities#applyOrientation(BufferedImage, int)} for + * applying TIFF.TAG_ORIENTATION. + *

+ * + * @param imageInput + * @param imageOutput + * @param degree Rotation amount, supports 90�, 180� and 270�. + * @throws IOException + */ + public static void rotatePages(ImageInputStream imageInput, ImageOutputStream imageOutput, int degree) + throws IOException { + rotatePage(imageInput, imageOutput, degree, -1); + } + + /** + * Rotates a page of a TIFF file by changing TIFF.TAG_ORIENTATION. + *

+ * NOTICE: TIFF.TAG_ORIENTATION is an advice how the image is meant do be + * displayed. Other metadata, such as width and height, relate to the image + * as how it is stored. The ImageIO TIFF plugin does not handle orientation. + * Use {@link TIFFUtilities#applyOrientation(BufferedImage, int)} for + * applying TIFF.TAG_ORIENTATION. + *

+ * + * @param imageInput + * @param imageOutput + * @param degree Rotation amount, supports 90�, 180� and 270�. + * @param pageIndex page which should be rotated or -1 for all pages. + * @throws IOException + */ + public static void rotatePage(ImageInputStream imageInput, ImageOutputStream imageOutput, int degree, int pageIndex) + throws IOException { + ImageInputStream input = null; + try { + List pages = getPages(imageInput); + if (pageIndex != -1) { + pages.get(pageIndex).rotate(degree); + } + else { + for (TIFFPage tiffPage : pages) { + tiffPage.rotate(degree); + } + } + writePages(imageOutput, pages); + } + finally { + if (input != null) { + input.close(); + } + } + } + + public static List getPages(ImageInputStream imageInput) throws IOException { + ArrayList pages = new ArrayList(); + + CompoundDirectory IFDs = (CompoundDirectory) new EXIFReader().read(imageInput); + + int pageCount = IFDs.directoryCount(); + for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) { + pages.add(new TIFFPage(IFDs.getDirectory(pageIndex), imageInput)); + } + + return pages; + } + + public static void writePages(ImageOutputStream imageOutput, List pages) throws IOException { + EXIFWriter exif = new EXIFWriter(); + long nextPagePos = imageOutput.getStreamPosition(); + if (nextPagePos == 0) { + exif.writeTIFFHeader(imageOutput); + nextPagePos = imageOutput.getStreamPosition(); + imageOutput.writeInt(0); + } + else { + // already has pages, so remember place of EOF to replace with + // IFD offset + nextPagePos -= 4; + } + + for (TIFFPage tiffPage : pages) { + long ifdOffset = tiffPage.write(imageOutput, exif); + + long tmp = imageOutput.getStreamPosition(); + imageOutput.seek(nextPagePos); + imageOutput.writeInt((int) ifdOffset); + imageOutput.seek(tmp); + nextPagePos = tmp; + imageOutput.writeInt(0); + } + } + + public static BufferedImage applyOrientation(BufferedImage input, int orientation) { + //TODO: Translations are currently off for non quadratic images + + boolean flipExtends = false; + AffineTransform transform = AffineTransform.getTranslateInstance(input.getWidth() / 2, input.getHeight() / 2); + switch (orientation) { + case TIFFBaseline.ORIENTATION_TOPLEFT: + // normal + return input; + case TIFFExtension.ORIENTATION_TOPRIGHT: + // flipped vertically + transform.scale(-1, 1); + break; + case TIFFExtension.ORIENTATION_BOTRIGHT: + // rotated 180� + transform.quadrantRotate(2); + break; + case TIFFExtension.ORIENTATION_BOTLEFT: + // flipped horizontally + transform.scale(1, -1); + break; + case TIFFExtension.ORIENTATION_LEFTTOP: + transform.scale(1, -1); + transform.quadrantRotate(1); + flipExtends = true; + break; + case TIFFExtension.ORIENTATION_RIGHTTOP: + // rotated 90� + transform.quadrantRotate(1); + flipExtends = true; + break; + case TIFFExtension.ORIENTATION_RIGHTBOT: + transform.scale(-1, 1); + transform.quadrantRotate(1); + flipExtends = true; + break; + case TIFFExtension.ORIENTATION_LEFTBOT: + // rotated 270� + transform.quadrantRotate(3); + flipExtends = true; + break; + } + + int w = input.getWidth(); + int h = input.getHeight(); + if (flipExtends) { + w = input.getHeight(); + h = input.getWidth(); + } + + BufferedImage output = new BufferedImage(w, h, input.getType()); + transform.translate(-h / 2, -w / 2); + ((Graphics2D) output.getGraphics()).drawImage(input, transform, null); + + return output; + } + + public static class TIFFPage { + private Directory IFD; + private ImageInputStream stream; + + private TIFFPage(Directory IFD, ImageInputStream stream) { + this.IFD = IFD; + this.stream = stream; + } + + private long write(ImageOutputStream outputStream, EXIFWriter exifWriter) throws IOException { + Entry stipOffsetsEntry = IFD.getEntryById(TIFF.TAG_STRIP_OFFSETS); + long[] offsets; + if (stipOffsetsEntry.valueCount() == 1) { + offsets = new long[] {(long) stipOffsetsEntry.getValue()}; + } + else { + offsets = (long[]) stipOffsetsEntry.getValue(); + } + + Entry stipByteCountsEntry = IFD.getEntryById(TIFF.TAG_STRIP_BYTE_COUNTS); + long[] byteCounts; + if (stipOffsetsEntry.valueCount() == 1) { + byteCounts = new long[] {(long) stipByteCountsEntry.getValue()}; + } + else { + byteCounts = (long[]) stipByteCountsEntry.getValue(); + } + + int[] newOffsets = new int[offsets.length]; + for (int i = 0; i < offsets.length; i++) { + newOffsets[i] = (int) outputStream.getStreamPosition(); + stream.seek(offsets[i]); + + byte[] buffer = new byte[(int) byteCounts[i]]; + stream.readFully(buffer); + outputStream.write(buffer); + } + + ArrayList newIFD = new ArrayList(); + Iterator it = IFD.iterator(); + while (it.hasNext()) { + newIFD.add(it.next()); + } + + newIFD.remove(stipOffsetsEntry); + newIFD.add(new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, newOffsets)); + return exifWriter.writeIFD(newIFD, outputStream); + } + + /** + * Rotates the image by changing TIFF.TAG_ORIENTATION. + *

+ * NOTICE: TIFF.TAG_ORIENTATION is an advice how the image is meant do + * be displayed. Other metadata, such as width and height, relate to the + * image as how it is stored. The ImageIO TIFF plugin does not handle + * orientation. Use + * {@link TIFFUtilities#applyOrientation(BufferedImage, int)} for + * applying TIFF.TAG_ORIENTATION. + *

+ * + * @param degree Rotation amount, supports 90�, 180� and 270�. + */ + public void rotate(int degree) { + Validate.isTrue(degree % 90 == 0 && degree > 0 && degree < 360, + "Only rotations by 90, 180 and 270 degree are supported"); + + ArrayList newIDFData = new ArrayList<>(); + Iterator it = IFD.iterator(); + while (it.hasNext()) { + newIDFData.add(it.next()); + } + + short orientation = TIFFBaseline.ORIENTATION_TOPLEFT; + Entry orientationEntry = IFD.getEntryById(TIFF.TAG_ORIENTATION); + if (orientationEntry != null) { + orientation = ((Number) orientationEntry.getValue()).shortValue(); + newIDFData.remove(orientationEntry); + } + + int steps = degree / 90; + for (int i = 0; i < steps; i++) { + switch (orientation) { + case TIFFBaseline.ORIENTATION_TOPLEFT: + orientation = TIFFExtension.ORIENTATION_RIGHTTOP; + break; + case TIFFExtension.ORIENTATION_TOPRIGHT: + orientation = TIFFExtension.ORIENTATION_RIGHTBOT; + break; + case TIFFExtension.ORIENTATION_BOTRIGHT: + orientation = TIFFExtension.ORIENTATION_LEFTBOT; + break; + case TIFFExtension.ORIENTATION_BOTLEFT: + orientation = TIFFExtension.ORIENTATION_LEFTTOP; + break; + case TIFFExtension.ORIENTATION_LEFTTOP: + orientation = TIFFExtension.ORIENTATION_TOPRIGHT; + break; + case TIFFExtension.ORIENTATION_RIGHTTOP: + orientation = TIFFExtension.ORIENTATION_BOTRIGHT; + break; + case TIFFExtension.ORIENTATION_RIGHTBOT: + orientation = TIFFExtension.ORIENTATION_BOTLEFT; + break; + case TIFFExtension.ORIENTATION_LEFTBOT: + orientation = TIFFBaseline.ORIENTATION_TOPLEFT; + break; + } + } + newIDFData.add(new TIFFImageWriter.TIFFEntry(TIFF.TAG_ORIENTATION, (short) orientation)); + IFD = new AbstractDirectory(newIDFData) { + }; + } + } +} diff --git a/contrib/src/test/java/com/twelvemonkeys/contrib/tiff/TIFFUtilitiesTest.java b/contrib/src/test/java/com/twelvemonkeys/contrib/tiff/TIFFUtilitiesTest.java new file mode 100644 index 00000000..1471000f --- /dev/null +++ b/contrib/src/test/java/com/twelvemonkeys/contrib/tiff/TIFFUtilitiesTest.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2013, 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.twelvemonkeys.contrib.tiff; + +import com.twelvemonkeys.imageio.plugins.tiff.TIFFExtension; +import com.twelvemonkeys.imageio.plugins.tiff.TIFFMedataFormat; +import com.twelvemonkeys.io.FileUtil; +import org.junit.Assert; +import org.junit.Test; +import org.w3c.dom.Node; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.ImageOutputStream; +import javax.xml.xpath.*; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; + +/** + * TIFFUtilitiesTest + * + * @author Oliver Schmidtmer + * @author last modified by $Author$ + * @version $Id$ + */ +public class TIFFUtilitiesTest { + + @Test + public void testMerge() throws IOException { + // Files from ImageIO TIFF Plugin + InputStream stream1 = getClassLoaderResource("/tiff/ccitt/group3_1d.tif").openStream(); + InputStream stream2 = getClassLoaderResource("/tiff/ccitt/group3_2d.tif").openStream(); + InputStream stream3 = getClassLoaderResource("/tiff/ccitt/group4.tif").openStream(); + + File file1 = File.createTempFile("imageiotest", ".tif"); + File file2 = File.createTempFile("imageiotest", ".tif"); + File file3 = File.createTempFile("imageiotest", ".tif"); + File output = File.createTempFile("imageiotest", ".tif"); + + byte[] data; + + data = FileUtil.read(stream1); + FileUtil.write(file1, data); + stream1.close(); + + data = FileUtil.read(stream2); + FileUtil.write(file2, data); + stream2.close(); + + data = FileUtil.read(stream3); + FileUtil.write(file3, data); + stream3.close(); + + List input = Arrays.asList(file1, file2, file3); + TIFFUtilities.merge(input, output); + + ImageInputStream iis = ImageIO.createImageInputStream(output); + ImageReader reader = ImageIO.getImageReaders(iis).next(); + reader.setInput(iis); + Assert.assertEquals(3, reader.getNumImages(true)); + + iis.close(); + output.delete(); + file1.delete(); + file2.delete(); + file3.delete(); + } + + @Test + public void testSplit() throws IOException { + InputStream inputStream = getClassLoaderResource("/contrib/tiff/multipage.tif").openStream(); + File inputFile = File.createTempFile("imageiotest", "tif"); + byte[] data = FileUtil.read(inputStream); + FileUtil.write(inputFile, data); + inputStream.close(); + + File outputDirectory = Files.createTempDirectory("imageio").toFile(); + + TIFFUtilities.split(inputFile, outputDirectory); + + ImageReader reader = ImageIO.getImageReadersByFormatName("TIF").next(); + + File[] outputFiles = outputDirectory.listFiles(); + Assert.assertEquals(3, outputFiles.length); + for (File outputFile : outputFiles) { + ImageInputStream iis = ImageIO.createImageInputStream(outputFile); + reader.setInput(iis); + Assert.assertEquals(1, reader.getNumImages(true)); + iis.close(); + outputFile.delete(); + } + outputDirectory.delete(); + inputFile.delete(); + } + + @Test + public void testRotate() throws IOException, XPathExpressionException { + ImageReader reader = ImageIO.getImageReadersByFormatName("TIF").next(); + + InputStream inputStream = getClassLoaderResource("/contrib/tiff/multipage.tif").openStream(); + File inputFile = File.createTempFile("imageiotest", ".tif"); + byte[] data = FileUtil.read(inputStream); + FileUtil.write(inputFile, data); + inputStream.close(); + + XPath xPath = XPathFactory.newInstance().newXPath(); + XPathExpression expression = xPath.compile("TIFFIFD/TIFFField[@number='274']/TIFFBytes/TIFFByte/@value"); + + // rotate all pages + ImageInputStream inputTest1 = ImageIO.createImageInputStream(inputFile); + File outputTest1 = File.createTempFile("imageiotest", ".tif"); + ImageOutputStream iosTest1 = ImageIO.createImageOutputStream(outputTest1); + TIFFUtilities.rotatePages(inputTest1, iosTest1, 90); + iosTest1.close(); + + ImageInputStream checkTest1 = ImageIO.createImageInputStream(outputTest1); + reader.setInput(checkTest1); + for (int i = 0; i < 3; i++) { + Node metaData = reader.getImageMetadata(i) + .getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME); + short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue(); + Assert.assertEquals(orientation, TIFFExtension.ORIENTATION_RIGHTTOP); + } + checkTest1.close(); + + // rotate single page further + ImageInputStream inputTest2 = ImageIO.createImageInputStream(outputTest1); + File outputTest2 = File.createTempFile("imageiotest", ".tif"); + ImageOutputStream iosTest2 = ImageIO.createImageOutputStream(outputTest2); + TIFFUtilities.rotatePage(inputTest2, iosTest2, 90, 1); + iosTest2.close(); + + ImageInputStream checkTest2 = ImageIO.createImageInputStream(outputTest2); + reader.setInput(checkTest2); + for (int i = 0; i < 3; i++) { + Node metaData = reader.getImageMetadata(i) + .getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME); + short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue(); + Assert.assertEquals(orientation, i == 1 + ? TIFFExtension.ORIENTATION_BOTRIGHT + : TIFFExtension.ORIENTATION_RIGHTTOP); + } + checkTest2.close(); + } + + @Test + public void testApplyOrientation() throws IOException { + InputStream inputStream = getClassLoaderResource("/contrib/tiff/multipage.tif").openStream(); + File inputFile = File.createTempFile("imageiotest", "tif"); + byte[] data = FileUtil.read(inputStream); + FileUtil.write(inputFile, data); + inputStream.close(); + + BufferedImage image = ImageIO.read(inputFile); + + // rotate by 90� + BufferedImage image90 = TIFFUtilities.applyOrientation(image, TIFFExtension.ORIENTATION_RIGHTTOP); + // rotate by 270� + BufferedImage image360 = TIFFUtilities.applyOrientation(image90, TIFFExtension.ORIENTATION_LEFTBOT); + + byte[] original = ((DataBufferByte) image.getData().getDataBuffer()).getData(); + byte[] rotated = ((DataBufferByte) image360.getData().getDataBuffer()).getData(); + + Assert.assertArrayEquals(original, rotated); + } + + protected URL getClassLoaderResource(final String pName) { + return getClass().getResource(pName); + } +} diff --git a/contrib/src/test/resources/contrib/tiff/multipage.tif b/contrib/src/test/resources/contrib/tiff/multipage.tif new file mode 100644 index 0000000000000000000000000000000000000000..2579cb34114229f2b12d3ca177e7ea146a6de98d GIT binary patch literal 824 zcmebEWzb?^V2EIl@MAET!;omfuT!paGQ1Wsp&45C;)L2f*Ax7$}T#BnApMjzD3=9w>~uh(KYq1%^`{i}=?D4p5vk n)iF>SAp9hy5>V_COeIXlBnAjON`PQZ8BBJFKw$C%rVa)GQI0-t literal 0 HcmV?d00001 diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFBaseline.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFBaseline.java index 6d9a8cee..3d62fd60 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFBaseline.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFBaseline.java @@ -35,7 +35,7 @@ package com.twelvemonkeys.imageio.plugins.tiff; * @author last modified by $Author: haraldk$ * @version $Id: TIFFBaseline.java,v 1.0 08.05.12 16:43 haraldk Exp$ */ -interface TIFFBaseline { +public interface TIFFBaseline { int COMPRESSION_NONE = 1; int COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE = 2; int COMPRESSION_PACKBITS = 32773; diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFExtension.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFExtension.java index f6444464..76e1f7a0 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFExtension.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFExtension.java @@ -35,7 +35,7 @@ package com.twelvemonkeys.imageio.plugins.tiff; * @author last modified by $Author: haraldk$ * @version $Id: TIFFExtension.java,v 1.0 08.05.12 16:45 haraldk Exp$ */ -interface TIFFExtension { +public interface TIFFExtension { /** CCITT T.4/Group 3 Fax compression. */ int COMPRESSION_CCITT_T4 = 3; /** CCITT T.6/Group 4 Fax compression. */ diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java index db5f9d5b..c06b8748 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java @@ -113,7 +113,7 @@ public final class TIFFImageWriter extends ImageWriterBase { // TODO: Allow appending/partly overwrite of existing file... } - static final class TIFFEntry extends AbstractEntry { + public static final class TIFFEntry extends AbstractEntry { // TODO: Expose a merge of this and the EXIFEntry class... private final short type; @@ -164,7 +164,7 @@ public final class TIFFImageWriter extends ImageWriterBase { throw new UnsupportedOperationException(String.format("Method guessType not implemented for value of type %s", value.getClass())); } - TIFFEntry(final int identifier, final Object value) { + public TIFFEntry(final int identifier, final Object value) { this(identifier, guessType(value), value); }