mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-18 00:00:03 -04:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 217d14095b | |||
| 1ee21061bd | |||
| 00b15746e4 | |||
| a42ccd031b | |||
| b4fde6ff17 | |||
| b275d7f777 | |||
| c544db9882 | |||
| 9da706dfbb | |||
| 977ecb0482 | |||
| 8143c957bf | |||
| a5d703b29b | |||
| b7265a5117 | |||
| a1769cd40b | |||
| d054ee9bb1 | |||
| 845944b25f | |||
| d12f9bdbbd | |||
| 3e9820bb22 | |||
| 2098e6a898 | |||
| feb20eefdd | |||
| 6977e52059 | |||
| 8299182f52 | |||
| 1ff764997b | |||
| f914d15677 | |||
| f32110c5b8 | |||
| e79f115ab4 | |||
| e956fedfcf | |||
| 61cbeb0a09 | |||
| 7634ca1261 | |||
| a67c0fb456 | |||
| 8c93be05a5 | |||
| afff6f78a8 | |||
| 3e0440b9f4 | |||
| fb9d9de6b0 | |||
| 2fb9a54618 | |||
| 06674d1273 | |||
| 7e88a6f7e3 | |||
| dd54793d3d | |||
| 96eb0d0b7f | |||
| 9e9f47a2fb | |||
| 9a6f4bba33 | |||
| d310b7c8e7 | |||
| 64668807e0 | |||
| 7430d0053a | |||
| a34a0e3f44 | |||
| c048928011 | |||
| f4ba4e081e | |||
| 9bb7b62987 | |||
| 552ab24658 | |||
| 52aa7e974b | |||
| cffc3af45c | |||
| 5952634671 | |||
| c3c23d0523 | |||
| aacad8a575 | |||
| 83a6d604a6 | |||
| e3bab84e82 | |||
| d607450ae4 | |||
| 037a47ca2a | |||
| 5c735674f0 | |||
| 822bea80b6 | |||
| e924fcefc0 | |||
| 0ab9294004 |
@@ -54,10 +54,10 @@ If you are one of the authors, or know one of the authors and/or the current lic
|
||||
* Read support for the following file types:
|
||||
* Monochrome, 1 channel, 1 bit
|
||||
* Indexed, 1 channel, 8 bit
|
||||
* Gray, 1 channel, 8 and 16 bit
|
||||
* Duotone, 1 channel, 8 and 16 bit
|
||||
* RGB, 3-4 channels, 8 and 16 bit
|
||||
* CMYK, 4-5 channels, 8 and 16 bit
|
||||
* Gray, 1 channel, 8, 16 and 32 bit
|
||||
* Duotone, 1 channel, 8, 16 and 32 bit
|
||||
* RGB, 3-4 channels, 8, 16 and 32 bit
|
||||
* CMYK, 4-5 channels, 8, 16 and 32 bit
|
||||
* Read support for the following compression types:
|
||||
* Uncompressed
|
||||
* RLE (PackBits)
|
||||
@@ -66,6 +66,7 @@ If you are one of the authors, or know one of the authors and/or the current lic
|
||||
* Thumbnail support
|
||||
* JPEG
|
||||
* RAW (RGB)
|
||||
* Support for "Large Document Format" (PSB)
|
||||
|
||||
#### Aldus/Adobe Tagged Image File Format (TIFF)
|
||||
|
||||
@@ -343,6 +344,10 @@ Build the project (using [Maven](http://maven.apache.org/download.cgi)):
|
||||
|
||||
$ mvn package
|
||||
|
||||
Currently, the only supported JDK for making a build is Oracle JDK 7.x.
|
||||
|
||||
It's possible to build using OpenJDK, but some tests will fail due to some minor differences between the color management systems used. You will need to either disable the tests in question, or build without tests altogether. To build using JDK 8, you need to pass `-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider` to revert to the color manangement system used in Java 7.
|
||||
|
||||
Because the unit tests needs quite a bit of memory to run, you might have to set the environment variable `MAVEN_OPTS`
|
||||
to give the Java process that runs Maven more memory. I suggest something like `-Xmx512m -XX:MaxPermSize=256m`.
|
||||
|
||||
@@ -379,12 +384,12 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<version>3.0-SNAPSHOT</version> <!-- Alternatively, build your own 3.0-something version -->
|
||||
<version>3.0-rc5</version> <!-- Alternatively, build your own 3.0-something version -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>3.0-SNAPSHOT</version> <!-- Alternatively, build your own 3.0-something version -->
|
||||
<version>3.0-rc5</version> <!-- Alternatively, build your own 3.0-something version -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -392,17 +397,41 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
|
||||
|
||||
To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path:
|
||||
|
||||
twelvemonkeys-common-lang-3.0-SNAPSHOT.jar
|
||||
twelvemonkeys-common-io-3.0-SNAPSHOT.jar
|
||||
twelvemonkeys-common-image-3.0-SNAPSHOT.jar
|
||||
twelvemonkeys-imageio-core-3.0-SNAPSHOT.jar
|
||||
twelvemonkeys-imageio-metadata-3.0-SNAPSHOT.jar
|
||||
twelvemonkeys-imageio-jpeg-3.0-SNAPSHOT.jar
|
||||
twelvemonkeys-imageio-tiff-3.0-SNAPSHOT.jar
|
||||
twelvemonkeys-common-lang-3.0-rc5.jar
|
||||
twelvemonkeys-common-io-3.0-rc5.jar
|
||||
twelvemonkeys-common-image-3.0-rc5.jar
|
||||
twelvemonkeys-imageio-core-3.0-rc5.jar
|
||||
twelvemonkeys-imageio-metadata-3.0-rc5.jar
|
||||
twelvemonkeys-imageio-jpeg-3.0-rc5.jar
|
||||
twelvemonkeys-imageio-tiff-3.0-rc5.jar
|
||||
|
||||
### Links to prebuilt binaries
|
||||
|
||||
There's no prebuilt binaries yet.
|
||||
Common dependencies
|
||||
* [common-lang-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.0-rc5/common-lang-3.0-rc5.jar)
|
||||
* [common-io-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.0-rc5/common-io-3.0-rc5.jar)
|
||||
* [common-image-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.0-rc5/common-image-3.0-rc5.jar)
|
||||
|
||||
ImageIO dependencies
|
||||
* [imageio-core-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.0-rc5/imageio-core-3.0-rc5.jar)
|
||||
* [imageio-metadata-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.0-rc5/imageio-metadata-3.0-rc5.jar)
|
||||
|
||||
ImageIO plugins
|
||||
* [imageio-jpeg-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.0-rc5/imageio-jpeg-3.0-rc5.jar)
|
||||
* [imageio-tiff-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.0-rc5/imageio-tiff-3.0-rc5.jar)
|
||||
* [imageio-psd-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.0-rc5/imageio-psd-3.0-rc5.jar)
|
||||
* [imageio-pict-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.0-rc5/imageio-pict-3.0-rc5.jar)
|
||||
* [imageio-iff-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.0-rc5/imageio-iff-3.0-rc5.jar)
|
||||
* [imageio-icns-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0-rc5/imageio-icns-3.0-rc5.jar)
|
||||
* [imageio-ico-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0-rc5/imageio-ico-3.0-rc5.jar)
|
||||
* [imageio-thumbsdb-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0-rc5/imageio-thumbsdb-3.0-rc5.jar)
|
||||
|
||||
ImageIO plugins requiring 3rd party libs
|
||||
* [imageio-batik-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.0-rc5/imageio-batik-3.0-rc5.jar)
|
||||
* [imageio-jmagick-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0-rc5/imageio-jmagick-3.0-rc5.jar)
|
||||
|
||||
Servlet support
|
||||
* [servlet-3.0-rc5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.0-rc5/servlet-3.0-rc5.jar)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>common-image</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -1,27 +1,18 @@
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
*
|
||||
* @author $author wmhakur$
|
||||
* @version $id: $
|
||||
* To change this template use Options | File Templates.
|
||||
*/
|
||||
public class ImageUtilTestCase extends TestCase {
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ImageUtilTestCase {
|
||||
|
||||
private final static String IMAGE_NAME = "/sunflower.jpg";
|
||||
private BufferedImage original;
|
||||
@@ -56,6 +47,7 @@ public class ImageUtilTestCase extends TestCase {
|
||||
}
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testToBufferedImageNull() {
|
||||
BufferedImage img = null;
|
||||
boolean threwRuntimeException = false;
|
||||
@@ -73,6 +65,7 @@ public class ImageUtilTestCase extends TestCase {
|
||||
assertTrue(threwRuntimeException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToBufferedImageTypeNull() {
|
||||
BufferedImage img = null;
|
||||
boolean threwRuntimeException = false;
|
||||
@@ -90,6 +83,7 @@ public class ImageUtilTestCase extends TestCase {
|
||||
assertTrue(threwRuntimeException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImageIsNotBufferedImage() {
|
||||
// Should not be a buffered image
|
||||
assertFalse(
|
||||
@@ -98,6 +92,7 @@ public class ImageUtilTestCase extends TestCase {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToBufferedImage() {
|
||||
BufferedImage sameAsImage = ImageUtil.toBuffered((RenderedImage) image);
|
||||
BufferedImage bufferedScaled = ImageUtil.toBuffered(scaled);
|
||||
@@ -111,10 +106,11 @@ public class ImageUtilTestCase extends TestCase {
|
||||
|
||||
// Hmmm...
|
||||
assertTrue(new Integer(42).equals(bufferedScaled.getProperty("lucky-number"))
|
||||
|| bufferedScaled.getPropertyNames() == null
|
||||
|| bufferedScaled.getPropertyNames().length == 0);
|
||||
|| bufferedScaled.getPropertyNames() == null
|
||||
|| bufferedScaled.getPropertyNames().length == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToBufferedImageType() {
|
||||
// Assumes image is TYPE_INT_ARGB
|
||||
BufferedImage converted = ImageUtil.toBuffered(image, BufferedImage.TYPE_BYTE_INDEXED);
|
||||
@@ -136,6 +132,7 @@ public class ImageUtilTestCase extends TestCase {
|
||||
assertEquals(image.getHeight(), convertedToo.getHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrightness() {
|
||||
final BufferedImage original = this.original;
|
||||
assertNotNull(original);
|
||||
@@ -181,7 +178,7 @@ public class ImageUtilTestCase extends TestCase {
|
||||
final BufferedImage brightenedMaxNegative = ImageUtil.toBuffered(ImageUtil.brightness(original, -2f));
|
||||
for (int y = 0; y < brightenedMaxNegative.getHeight(); y++) {
|
||||
for (int x = 0; x < brightenedMaxNegative.getWidth(); x++) {
|
||||
assertEquals(0x0, brightenedMaxNegative.getRGB(x, y) & 0x00FFFFFF);
|
||||
assertEquals(0x0, brightenedMaxNegative.getRGB(x, y) & 0x00FFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +212,7 @@ public class ImageUtilTestCase extends TestCase {
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testContrast() {
|
||||
final BufferedImage original = this.original;
|
||||
|
||||
@@ -273,7 +270,6 @@ public class ImageUtilTestCase extends TestCase {
|
||||
else {
|
||||
assertTrue("Contrast should be increased or same", oB <= cB && cB <= dB);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// Assumed: Only primary colors (w/b/r/g/b/c/y/m)
|
||||
@@ -337,7 +333,7 @@ public class ImageUtilTestCase extends TestCase {
|
||||
int r = rgb >> 16 & 0xFF;
|
||||
int g = rgb >> 8 & 0xFF;
|
||||
int b = rgb & 0xFF;
|
||||
assertTrue("Minimum contrast should be all gray", r == 127 && g == 127 &&b == 127);
|
||||
assertTrue("Minimum contrast should be all gray", r == 127 && g == 127 && b == 127);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,6 +365,7 @@ public class ImageUtilTestCase extends TestCase {
|
||||
*/
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharpen() {
|
||||
final BufferedImage original = this.original;
|
||||
|
||||
@@ -390,10 +387,10 @@ public class ImageUtilTestCase extends TestCase {
|
||||
final BufferedImage sharpenedDefault = ImageUtil.sharpen(original, 0.3f);
|
||||
final BufferedImage sharpenedMore = ImageUtil.sharpen(original, 1.3f);
|
||||
|
||||
long diffOriginal = 0;
|
||||
long diffSharpened = 0;
|
||||
long diffDefault = 0;
|
||||
long diffMore = 0;
|
||||
// long diffOriginal = 0;
|
||||
// long diffSharpened = 0;
|
||||
// long diffDefault = 0;
|
||||
// long diffMore = 0;
|
||||
|
||||
long absDiffOriginal = 0;
|
||||
long absDiffSharpened = 0;
|
||||
@@ -412,10 +409,10 @@ public class ImageUtilTestCase extends TestCase {
|
||||
int pdRGB = 0x00FFFFFF & sharpenedDefault.getRGB(x - 1, y);
|
||||
int pmRGB = 0x00FFFFFF & sharpenedMore.getRGB(x - 1, y);
|
||||
|
||||
diffOriginal += poRGB - oRGB;
|
||||
diffSharpened += psRGB - sRGB;
|
||||
diffDefault += pdRGB - dRGB;
|
||||
diffMore += pmRGB - mRGB;
|
||||
// diffOriginal += poRGB - oRGB;
|
||||
// diffSharpened += psRGB - sRGB;
|
||||
// diffDefault += pdRGB - dRGB;
|
||||
// diffMore += pmRGB - mRGB;
|
||||
|
||||
absDiffOriginal += Math.abs(poRGB - oRGB);
|
||||
absDiffSharpened += Math.abs(psRGB - sRGB);
|
||||
@@ -424,10 +421,6 @@ public class ImageUtilTestCase extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
//*
|
||||
showEm(original, notSharpened, sharpened, sharpenedDefault, sharpenedMore, "sharpen");
|
||||
//*/
|
||||
|
||||
// assertEquals("Difference should not change", diffOriginal, diffSharpened);
|
||||
assertTrue("Abs difference should increase", absDiffOriginal < absDiffSharpened);
|
||||
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
||||
@@ -438,62 +431,7 @@ public class ImageUtilTestCase extends TestCase {
|
||||
assertTrue("Abs difference should increase", absDiffSharpened < absDiffMore);
|
||||
}
|
||||
|
||||
private void showEm(final BufferedImage pOriginal, final BufferedImage pNotSharpened, final BufferedImage pSharpened, final BufferedImage pSharpenedDefault, final BufferedImage pSharpenedMore, final String pTitle) {
|
||||
if (pOriginal != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
public void run() {
|
||||
JFrame frame = new JFrame("Sunflower - " + pTitle);
|
||||
frame.setSize(pOriginal.getWidth() * 4, pOriginal.getHeight() * 2);
|
||||
|
||||
Canvas canvas = new Canvas() {
|
||||
public void paint(Graphics g) {
|
||||
// Draw original for comparison
|
||||
g.drawImage(pOriginal, 0, 0, null);
|
||||
|
||||
// This should look like original
|
||||
g.drawImage(pNotSharpened, 0, pOriginal.getHeight(), null);
|
||||
|
||||
// Different versions
|
||||
g.drawImage(pSharpened, pOriginal.getWidth(), 0, null);
|
||||
g.drawImage(pSharpenedDefault, pOriginal.getWidth() * 2, 0, null);
|
||||
g.drawImage(pSharpenedMore, pOriginal.getWidth() * 3, 0, null);
|
||||
}
|
||||
};
|
||||
|
||||
frame.getContentPane().add(canvas);
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
synchronized (ImageUtilTestCase.this) {
|
||||
ImageUtilTestCase.this.notify();
|
||||
}
|
||||
}
|
||||
});
|
||||
frame.setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
synchronized (ImageUtilTestCase.this) {
|
||||
try {
|
||||
ImageUtilTestCase.this.wait();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlur() {
|
||||
final BufferedImage original = this.original;
|
||||
|
||||
@@ -515,17 +453,16 @@ public class ImageUtilTestCase extends TestCase {
|
||||
final BufferedImage blurredDefault = ImageUtil.blur(original, 1.5f);
|
||||
final BufferedImage blurredMore = ImageUtil.blur(original, 3f);
|
||||
|
||||
long diffOriginal = 0;
|
||||
long diffBlurred = 0;
|
||||
long diffDefault = 0;
|
||||
long diffMore = 0;
|
||||
// long diffOriginal = 0;
|
||||
// long diffBlurred = 0;
|
||||
// long diffDefault = 0;
|
||||
// long diffMore = 0;
|
||||
|
||||
long absDiffOriginal = 0;
|
||||
long absDiffBlurred = 0;
|
||||
long absDiffDefault = 0;
|
||||
long absDiffMore = 0;
|
||||
|
||||
|
||||
for (int y = 0; y < original.getHeight(); y++) {
|
||||
for (int x = 1; x < original.getWidth(); x++) {
|
||||
int oRGB = 0x00FFFFFF & original.getRGB(x, y);
|
||||
@@ -538,10 +475,10 @@ public class ImageUtilTestCase extends TestCase {
|
||||
int pdRGB = 0x00FFFFFF & blurredDefault.getRGB(x - 1, y);
|
||||
int pmRGB = 0x00FFFFFF & blurredMore.getRGB(x - 1, y);
|
||||
|
||||
diffOriginal += poRGB - oRGB;
|
||||
diffBlurred += pbRGB - bRGB;
|
||||
diffDefault += pdRGB - dRGB;
|
||||
diffMore += pmRGB - mRGB;
|
||||
// diffOriginal += poRGB - oRGB;
|
||||
// diffBlurred += pbRGB - bRGB;
|
||||
// diffDefault += pdRGB - dRGB;
|
||||
// diffMore += pmRGB - mRGB;
|
||||
|
||||
absDiffOriginal += Math.abs(poRGB - oRGB);
|
||||
absDiffBlurred += Math.abs(pbRGB - bRGB);
|
||||
@@ -550,8 +487,6 @@ public class ImageUtilTestCase extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
showEm(original, notBlurred, blurred, blurredDefault, blurredMore, "blur");
|
||||
|
||||
// assertEquals("Difference should not change", diffOriginal, diffBlurred);
|
||||
assertTrue(String.format("Abs difference should decrease: %s <= %s", absDiffOriginal, absDiffBlurred), absDiffOriginal > absDiffBlurred);
|
||||
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
||||
@@ -562,6 +497,7 @@ public class ImageUtilTestCase extends TestCase {
|
||||
assertTrue("Abs difference should decrease", absDiffBlurred > absDiffMore);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexImage() {
|
||||
BufferedImage sunflower = original;
|
||||
|
||||
@@ -571,4 +507,4 @@ public class ImageUtilTestCase extends TestCase {
|
||||
assertNotNull("Image was null", image);
|
||||
assertTrue(image.getColorModel() instanceof IndexColorModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
@@ -9,6 +10,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* ResampleOpTestCase
|
||||
*
|
||||
@@ -16,7 +19,7 @@ import java.util.List;
|
||||
* @author last modified by $Author: haku $
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/image/ResampleOpTestCase.java#1 $
|
||||
*/
|
||||
public class ResampleOpTestCase extends TestCase {
|
||||
public class ResampleOpTestCase {
|
||||
|
||||
protected BufferedImage createImage(final int pWidth, final int pHeigth) {
|
||||
return createImage(pWidth, pHeigth, BufferedImage.TYPE_INT_ARGB);
|
||||
@@ -36,6 +39,7 @@ public class ResampleOpTestCase extends TestCase {
|
||||
return image;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateImage() {
|
||||
// Sanity test the create method
|
||||
BufferedImage image = createImage(79, 84);
|
||||
@@ -94,170 +98,225 @@ public class ResampleOpTestCase extends TestCase {
|
||||
}
|
||||
|
||||
// 1x1
|
||||
@Test
|
||||
public void testResample1x1Point() {
|
||||
assertResample(createImage(1, 1), 10, 11, ResampleOp.FILTER_POINT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample1x1Box() {
|
||||
assertResample(createImage(1, 1), 10, 11, ResampleOp.FILTER_BOX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample1x1Triangle() {
|
||||
assertResample(createImage(1, 1), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample1x1Lanczos() {
|
||||
assertResample(createImage(1, 1), 7, 49, ResampleOp.FILTER_LANCZOS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample1x1Gaussian() {
|
||||
assertResample(createImage(1, 1), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample1x1Sinc() {
|
||||
assertResample(createImage(1, 1), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
||||
}
|
||||
|
||||
// 2x2
|
||||
@Test
|
||||
public void testResample2x2Point() {
|
||||
assertResample(createImage(2, 2), 10, 11, ResampleOp.FILTER_POINT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample2x2Box() {
|
||||
assertResample(createImage(2, 2), 10, 11, ResampleOp.FILTER_BOX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample2x2Triangle() {
|
||||
assertResample(createImage(2, 2), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample2x2Lanczos() {
|
||||
assertResample(createImage(2, 2), 7, 49, ResampleOp.FILTER_LANCZOS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample2x2Gaussian() {
|
||||
assertResample(createImage(2, 2), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample2x2Sinc() {
|
||||
assertResample(createImage(2, 2), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
||||
}
|
||||
|
||||
// 3x3
|
||||
@Test
|
||||
public void testResample3x3Point() {
|
||||
assertResample(createImage(3, 3), 10, 11, ResampleOp.FILTER_POINT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample3x3Box() {
|
||||
assertResample(createImage(3, 3), 10, 11, ResampleOp.FILTER_BOX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample3x3Triangle() {
|
||||
assertResample(createImage(3, 3), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample3x3Lanczos() {
|
||||
assertResample(createImage(3, 3), 7, 49, ResampleOp.FILTER_LANCZOS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample3x3Gaussian() {
|
||||
assertResample(createImage(3, 3), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample3x3Sinc() {
|
||||
assertResample(createImage(3, 3), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
||||
}
|
||||
|
||||
// 4x4
|
||||
@Test
|
||||
public void testResample4x4Point() {
|
||||
assertResample(createImage(4, 4), 10, 11, ResampleOp.FILTER_POINT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample4x4Box() {
|
||||
assertResample(createImage(4, 4), 10, 11, ResampleOp.FILTER_BOX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample4x4Triangle() {
|
||||
assertResample(createImage(4, 4), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample4x4Lanczos() {
|
||||
assertResample(createImage(4, 4), 7, 49, ResampleOp.FILTER_LANCZOS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample4x4Gaussian() {
|
||||
assertResample(createImage(4, 4), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample4x4Sinc() {
|
||||
assertResample(createImage(4, 4), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
||||
}
|
||||
|
||||
// 20x20
|
||||
@Test
|
||||
public void testResample20x20Point() {
|
||||
assertResample(createImage(20, 20), 10, 11, ResampleOp.FILTER_POINT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample20x20Box() {
|
||||
assertResample(createImage(20, 20), 10, 11, ResampleOp.FILTER_BOX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample20x20Triangle() {
|
||||
assertResample(createImage(20, 20), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample20x20Lanczos() {
|
||||
assertResample(createImage(20, 20), 7, 49, ResampleOp.FILTER_LANCZOS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample20x20Gaussian() {
|
||||
assertResample(createImage(20, 20), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample20x20Sinc() {
|
||||
assertResample(createImage(20, 20), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
||||
}
|
||||
|
||||
// 200x160
|
||||
@Test
|
||||
public void testResample200x160Point() {
|
||||
assertResample(createImage(200, 160), 10, 11, ResampleOp.FILTER_POINT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample200x160Box() {
|
||||
assertResample(createImage(200, 160), 10, 11, ResampleOp.FILTER_BOX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample200x160Triangle() {
|
||||
assertResample(createImage(200, 160), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample200x160Lanczos() {
|
||||
assertResample(createImage(200, 160), 7, 49, ResampleOp.FILTER_LANCZOS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample200x160Gaussian() {
|
||||
assertResample(createImage(200, 160), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResample200x160Sinc() {
|
||||
assertResample(createImage(200, 160), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
||||
}
|
||||
|
||||
// Test 10x10 -> 15x5 with different algorithms and types
|
||||
@Test
|
||||
public void testResamplePoint() {
|
||||
assertResampleBufferedImageTypes(ResampleOp.FILTER_POINT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResampleBox() {
|
||||
assertResampleBufferedImageTypes(ResampleOp.FILTER_BOX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResampleTriangle() {
|
||||
assertResampleBufferedImageTypes(ResampleOp.FILTER_TRIANGLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResampleLanczos() {
|
||||
assertResampleBufferedImageTypes(ResampleOp.FILTER_LANCZOS);
|
||||
}
|
||||
|
||||
@Ignore("Not for general unit testing")
|
||||
@Test
|
||||
public void testTime() {
|
||||
int iterations = 1000;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
assertResample(createImage(50, 50), 33, 33, ResampleOp.FILTER_LANCZOS);
|
||||
}
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
assertResample(createImage(512, 512), 145, 145, ResampleOp.FILTER_LANCZOS);
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.printf("time: %d ms, avg %s ms%n", end - start, (end - start) / (double) iterations);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>common-io</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -156,9 +156,9 @@ public final class DecoderStream extends FilterInputStream {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Skipped can never be more than avail, which is
|
||||
// an int, so the cast is safe
|
||||
// NOTE: Skipped can never be more than avail, which is an int, so the cast is safe
|
||||
int skipped = (int) Math.min(pLength - total, buffer.remaining());
|
||||
buffer.position(buffer.position() + skipped);
|
||||
total += skipped;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-batik</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
||||
|
||||
+11
@@ -29,10 +29,13 @@
|
||||
package com.twelvemonkeys.imageio.plugins.svg;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.image.ImagingOpException;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -77,6 +80,7 @@ public class SVGImageReaderTestCase extends ImageReaderAbstractTestCase<SVGImage
|
||||
return Arrays.asList("image/svg+xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testReadWithSizeParam() {
|
||||
try {
|
||||
@@ -99,4 +103,11 @@ public class SVGImageReaderTestCase extends ImageReaderAbstractTestCase<SVGImage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Source region reading not supported")
|
||||
@Override
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
}
|
||||
+10
@@ -29,9 +29,12 @@
|
||||
package com.twelvemonkeys.imageio.plugins.wmf;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -76,4 +79,11 @@ public class WMFImageReaderTestCase extends ImageReaderAbstractTestCase<WMFImage
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/x-wmf", "application/x-msmetafile");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Source region reading not supported")
|
||||
@Override
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
||||
|
||||
@@ -157,6 +157,7 @@ public final class ColorSpaces {
|
||||
* to avoid unnecessary array copying.
|
||||
*/
|
||||
private static boolean fixProfileXYZTag(ICC_Profile profile, final int tagSignature) {
|
||||
// TODO: This blows up on OpenJDK... Bug?
|
||||
byte[] data = profile.getData(tagSignature);
|
||||
|
||||
// The CMM expects 0x64 65 73 63 ('XYZ ') but is 0x17 A5 05 B8..?
|
||||
|
||||
+41
-5
@@ -64,7 +64,7 @@ import static org.mockito.Mockito.*;
|
||||
* @version $Id: ImageReaderAbstractTestCase.java,v 1.0 Apr 1, 2008 10:36:46 PM haraldk Exp$
|
||||
*/
|
||||
public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
||||
// TODO: Should we really test if he provider is installed?
|
||||
// TODO: Should we really test if the provider is installed?
|
||||
// - Pro: Tests the META-INF/services config
|
||||
// - Con: Not all providers should be installed at runtime...
|
||||
|
||||
@@ -548,6 +548,44 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
||||
assertEquals("Read image has wrong height: " + image.getHeight(), 10, image.getHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
// Default invocation
|
||||
assertReadWithSourceRegionParamEqualImage(new Rectangle(3, 3, 9, 9), getTestData().get(0), 0);
|
||||
}
|
||||
|
||||
protected void assertReadWithSourceRegionParamEqualImage(final Rectangle r, final TestData data, final int imageIndex) throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
reader.setInput(data.getInputStream());
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
|
||||
// Read full image and get sub image for comparison
|
||||
final BufferedImage roi = reader.read(imageIndex, param).getSubimage(r.x, r.y, r.width, r.height);
|
||||
|
||||
param.setSourceRegion(r);
|
||||
|
||||
final BufferedImage image = reader.read(imageIndex, param);
|
||||
|
||||
// try {
|
||||
// SwingUtilities.invokeAndWait(new Runnable() {
|
||||
// public void run() {
|
||||
// JPanel panel = new JPanel(new FlowLayout());
|
||||
// panel.add(new JLabel(new BufferedImageIcon(roi, r.width * 10, r.height * 10, true)));
|
||||
// panel.add(new JLabel(new BufferedImageIcon(image, r.width * 10, r.height * 10, true)));
|
||||
// JOptionPane.showConfirmDialog(null, panel);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// catch (Exception e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
|
||||
assertNotNull("Image was null!", image);
|
||||
assertEquals("Read image has wrong width: " + image.getWidth(), r.width, image.getWidth());
|
||||
assertEquals("Read image has wrong height: " + image.getHeight(), r.height, image.getHeight());
|
||||
assertImageDataEquals("Images differ", roi, image);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithSizeAndSourceRegionParam() {
|
||||
// TODO: Is this test correct???
|
||||
@@ -597,10 +635,8 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
assertNotNull("Image was null!", image);
|
||||
assertEquals("Read image has wrong width: " + image.getWidth(),
|
||||
5, image.getWidth());
|
||||
assertEquals("Read image has wrong height: " + image.getHeight(),
|
||||
5, image.getHeight());
|
||||
assertEquals("Read image has wrong width: " + image.getWidth(), 5, image.getWidth());
|
||||
assertEquals("Read image has wrong height: " + image.getHeight(), 5, image.getHeight());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-icns</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
|
||||
|
||||
+7
@@ -138,4 +138,11 @@ public class ICNSImageReaderTest extends ImageReaderAbstractTestCase {
|
||||
public void testReadWithSubsampleParamPixels() throws IOException {
|
||||
super.testReadWithSubsampleParamPixels();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Source region reading not supported")
|
||||
@Override
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-ico</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: ICO plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-iff</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jmagick</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JMagick Plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
|
||||
|
||||
+13
-8
@@ -212,7 +212,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
Iterator<ImageTypeSpecifier> types = delegate.getImageTypes(imageIndex);
|
||||
JPEGColorSpace csType = getSourceCSType(getAdobeDCT(), getSOF());
|
||||
JPEGColorSpace csType = getSourceCSType(getJFIF(), getAdobeDCT(), getSOF());
|
||||
|
||||
if (types == null || !types.hasNext() || csType == JPEGColorSpace.CMYK || csType == JPEGColorSpace.YCCK) {
|
||||
ArrayList<ImageTypeSpecifier> typeList = new ArrayList<ImageTypeSpecifier>();
|
||||
@@ -266,7 +266,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
// Otherwise, consult the image metadata
|
||||
JPEGColorSpace csType = getSourceCSType(getAdobeDCT(), getSOF());
|
||||
JPEGColorSpace csType = getSourceCSType(getJFIF(), getAdobeDCT(), getSOF());
|
||||
|
||||
switch (csType) {
|
||||
case CMYK:
|
||||
@@ -318,7 +318,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
ICC_Profile profile = getEmbeddedICCProfile(false);
|
||||
AdobeDCTSegment adobeDCT = getAdobeDCT();
|
||||
SOFSegment sof = getSOF();
|
||||
JPEGColorSpace sourceCSType = getSourceCSType(adobeDCT, sof);
|
||||
JPEGColorSpace sourceCSType = getSourceCSType(getJFIF(), adobeDCT, sof);
|
||||
|
||||
// We need to apply ICC profile unless the profile is sRGB/default gray (whatever that is)
|
||||
// - or only filter out the bad ICC profiles in the JPEGSegmentImageInputStream.
|
||||
@@ -438,7 +438,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
// Unfortunately looping is slower than reading all at once, but
|
||||
// that requires 2 x memory or more, so a few steps is an ok compromise I guess
|
||||
try {
|
||||
final int step = Math.max(1024, srcRegion.height / 10); // * param.getSourceYSubsampling(); // TODO: Using a multiple of 8 is probably a good idea for JPEG
|
||||
final int step = Math.max(1024, srcRegion.height / 10); // TODO: Using a multiple of 8 is probably a good idea for JPEG
|
||||
final int srcMaxY = srcRegion.y + srcRegion.height;
|
||||
int destY = dstRegion.y;
|
||||
|
||||
@@ -448,6 +448,11 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
// Let the progress delegator handle progress, using corrected range
|
||||
progressDelegator.updateProgressRange(100f * (y + scan) / srcRegion.height);
|
||||
|
||||
// Make sure subsampling is within bounds
|
||||
if (scan <= param.getSubsamplingYOffset()) {
|
||||
param.setSourceSubsampling(param.getSourceXSubsampling(), param.getSourceYSubsampling(), param.getSubsamplingXOffset(), scan - 1);
|
||||
}
|
||||
|
||||
Rectangle subRegion = new Rectangle(srcRegion.x, y, srcRegion.width, scan);
|
||||
param.setSourceRegion(subRegion);
|
||||
Raster raster = delegate.readRaster(imageIndex, param); // non-converted
|
||||
@@ -499,7 +504,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
return image;
|
||||
}
|
||||
|
||||
static JPEGColorSpace getSourceCSType(AdobeDCTSegment adobeDCT, final SOFSegment startOfFrame) throws IIOException {
|
||||
static JPEGColorSpace getSourceCSType(JFIFSegment jfif, AdobeDCTSegment adobeDCT, final SOFSegment startOfFrame) throws IIOException {
|
||||
/*
|
||||
ADAPTED from http://download.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html:
|
||||
|
||||
@@ -586,7 +591,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
return JPEGColorSpace.RGB;
|
||||
return jfif != null ? JPEGColorSpace.YCbCr : JPEGColorSpace.RGB;
|
||||
}
|
||||
case 4:
|
||||
if (startOfFrame.components[0].id == 1 && startOfFrame.components[1].id == 2 && startOfFrame.components[2].id == 3 && startOfFrame.components[3].id == 4) {
|
||||
@@ -629,10 +634,10 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
// See ColorConvertOp#filter(Raster, WritableRaster)
|
||||
|
||||
if (profile != null && profile.getProfileClass() != ICC_Profile.CLASS_DISPLAY) {
|
||||
byte[] profileData = profile.getData(); // Need to clone entire profile, due to a JDK 7 bug
|
||||
byte[] profileData = profile.getData(); // Need to clone entire profile, due to a OpenJDK bug
|
||||
|
||||
if (profileData[ICC_Profile.icHdrRenderingIntent] == ICC_Profile.icPerceptual) {
|
||||
processWarningOccurred("ICC profile is Perceptual but Display class, treating as Display class");
|
||||
processWarningOccurred("ICC profile is Perceptual, ignoring, treating as Display class");
|
||||
|
||||
intToBigEndian(ICC_Profile.icSigDisplayClass, profileData, ICC_Profile.icHdrDeviceClass); // Header is first
|
||||
|
||||
|
||||
+15
-10
@@ -89,20 +89,25 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
|
||||
// Scan forward
|
||||
while (true) {
|
||||
long realPosition = stream.getStreamPosition();
|
||||
int marker = stream.readUnsignedShort();
|
||||
|
||||
// Skip over weird 0x00 padding, but leave in stream, read seems to handle it well with a warning
|
||||
int trash = 0;
|
||||
while (marker == 0) {
|
||||
marker = stream.readUnsignedShort();
|
||||
trash += 2;
|
||||
int marker = stream.readUnsignedByte();
|
||||
|
||||
// Skip bad padding before the marker
|
||||
while (marker != 0xff) {
|
||||
marker = stream.readUnsignedByte();
|
||||
trash++;
|
||||
realPosition++;
|
||||
}
|
||||
|
||||
if (marker == 0x00ff) {
|
||||
trash++;
|
||||
marker = 0xff00 | stream.readUnsignedByte();
|
||||
if (trash != 0) {
|
||||
// NOTE: We previously allowed these bytes to pass through to the native reader, as it could cope
|
||||
// and issued the correct warning. However, the native metadata chokes on it, so we'll mask it out.
|
||||
// TODO: Issue warning from the JPEGImageReader, telling how many bytes we skipped
|
||||
}
|
||||
|
||||
marker = 0xff00 | stream.readUnsignedByte();
|
||||
|
||||
// Skip over 0xff padding between markers
|
||||
while (marker == 0xffff) {
|
||||
realPosition++;
|
||||
@@ -113,7 +118,7 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
|
||||
// TODO: Refactor to make various segments optional, we probably only want the "Adobe" APP14 segment, 'Exif' APP1 and very few others
|
||||
if (isAppSegmentMarker(marker) && !(marker == JPEG.APP1 && isAppSegmentWithId("Exif", stream)) && marker != JPEG.APP14) {
|
||||
int length = stream.readUnsignedShort(); // Length including length field itself
|
||||
stream.seek(realPosition + trash + 2 + length); // Skip marker (2) + length
|
||||
stream.seek(realPosition + 2 + length); // Skip marker (2) + length
|
||||
}
|
||||
else {
|
||||
if (marker == JPEG.EOI) {
|
||||
@@ -129,7 +134,7 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
|
||||
}
|
||||
else {
|
||||
// Length including length field itself
|
||||
length = trash + stream.readUnsignedShort() + 2;
|
||||
length = stream.readUnsignedShort() + 2;
|
||||
}
|
||||
|
||||
segment = new Segment(marker, realPosition, segment.end(), length);
|
||||
|
||||
+158
-11
@@ -30,6 +30,7 @@ package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
import org.hamcrest.core.IsInstanceOf;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mockito.internal.matchers.GreaterThan;
|
||||
import org.w3c.dom.Element;
|
||||
@@ -86,7 +87,8 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
new TestData(getClassLoaderResource("/jpeg/cmyk-sample.jpg"), new Dimension(160, 227)),
|
||||
new TestData(getClassLoaderResource("/jpeg/cmyk-sample-multiple-chunk-icc.jpg"), new Dimension(2707, 3804)),
|
||||
new TestData(getClassLoaderResource("/jpeg/jfif-jfxx-thumbnail-olympus-d320l.jpg"), new Dimension(640, 480)),
|
||||
new TestData(getClassLoaderResource("/jpeg/jfif-padded-segments.jpg"), new Dimension(20, 45))
|
||||
new TestData(getClassLoaderResource("/jpeg/jfif-padded-segments.jpg"), new Dimension(20, 45)),
|
||||
new TestData(getClassLoaderResource("/jpeg/0x00-to-0xFF-between-segments.jpg"), new Dimension(16, 16))
|
||||
);
|
||||
|
||||
// More test data in specific tests below
|
||||
@@ -107,7 +109,6 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@Override
|
||||
protected Class<JPEGImageReader> getReaderClass() {
|
||||
return JPEGImageReader.class;
|
||||
@@ -329,6 +330,32 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
verify(warningListener).warningOccurred(eq(reader), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testYCbCrNotSubsampledNonstandardChannelIndexes() throws IOException {
|
||||
// Regression: Make sure 3 channel, non-subsampled JFIF, defaults to YCbCr, even if unstandard channel indexes
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/jfif-ycbcr-no-subsampling-intel.jpg")));
|
||||
|
||||
assertEquals(600, reader.getWidth(0));
|
||||
assertEquals(600, reader.getHeight(0));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(8, 8));
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(8, image.getWidth());
|
||||
assertEquals(8, image.getHeight());
|
||||
|
||||
// QnD test: Make sure all pixels are white (if treated as RGB, they will be pink-ish)
|
||||
for (int y = 0; y < image.getHeight(); y++) {
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
assertEquals(0xffffff, image.getRGB(x, y) & 0xffffff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCorbisRGB() throws IOException {
|
||||
// Special case, throws exception below without special treatment
|
||||
@@ -486,9 +513,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
// Validate strip colors
|
||||
for (int i = 0; i < strip.getWidth() / 128; i++) {
|
||||
int actualRGB = strip.getRGB(i * 128, 4);
|
||||
assertEquals((actualRGB >> 16) & 0xff, (expectedRGB[i] >> 16) & 0xff, 5);
|
||||
assertEquals((actualRGB >> 8) & 0xff, (expectedRGB[i] >> 8) & 0xff, 5);
|
||||
assertEquals((actualRGB) & 0xff, (expectedRGB[i]) & 0xff, 5);
|
||||
assertRGBEquals(expectedRGB[i], actualRGB);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,9 +541,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
// Validate strip colors
|
||||
for (int i = 0; i < thumbnail.getWidth() / 8; i++) {
|
||||
int actualRGB = thumbnail.getRGB(i * 8, 4);
|
||||
assertEquals((actualRGB >> 16) & 0xff, (expectedRGB[i] >> 16) & 0xff, 5);
|
||||
assertEquals((actualRGB >> 8) & 0xff, (expectedRGB[i] >> 8) & 0xff, 5);
|
||||
assertEquals((actualRGB) & 0xff, (expectedRGB[i]) & 0xff, 5);
|
||||
assertRGBEquals(expectedRGB[i], actualRGB);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -653,12 +676,135 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
// Validate strip colors
|
||||
for (int i = 0; i < image.getWidth() / 10; i++) {
|
||||
int actualRGB = image.getRGB(i * 10, 7);
|
||||
assertEquals((actualRGB >> 16) & 0xff, (expectedRGB[i] >> 16) & 0xff, 5);
|
||||
assertEquals((actualRGB >> 8) & 0xff, (expectedRGB[i] >> 8) & 0xff, 5);
|
||||
assertEquals((actualRGB ) & 0xff, (expectedRGB[i] ) & 0xff, 5);
|
||||
assertRGBEquals(expectedRGB[i], actualRGB);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Slightly fuzzy RGB equals method. Tolerance +/-5 steps.
|
||||
*/
|
||||
private void assertRGBEquals(int expectedRGB, int actualRGB) {
|
||||
assertEquals((expectedRGB >> 16) & 0xff, (actualRGB >> 16) & 0xff, 5);
|
||||
assertEquals((expectedRGB >> 8) & 0xff, (actualRGB >> 8) & 0xff, 5);
|
||||
assertEquals((expectedRGB ) & 0xff, (actualRGB ) & 0xff, 5);
|
||||
}
|
||||
|
||||
// Regression: Test subsampling offset within of bounds
|
||||
// NOTE: These tests assumes the reader will read at least 1024 scanlines (if available) each iteration,
|
||||
// this might change in the future. If so, the tests will no longer test what tey are supposed to....
|
||||
@Test
|
||||
public void testReadSubsamplingBounds1028() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/read-error1028.jpg")));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceSubsampling(3, 3, 1, 1);
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testReadSubsamplingNotSkippingLines1028() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/read-error1028.jpg")));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceSubsampling(3, 3, 1, 1);
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
|
||||
// Make sure correct color is actually read, not just left empty
|
||||
assertRGBEquals(0xfefefd, image.getRGB(0, image.getHeight() - 2));
|
||||
assertRGBEquals(0xfefefd, image.getRGB(0, image.getHeight() - 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSubsamplingBounds1027() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/read-error1027.jpg")));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceSubsampling(3, 3, 2, 2);
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
|
||||
// Make sure correct color is actually read, not just left empty
|
||||
assertRGBEquals(0xfefefd, image.getRGB(0, image.getHeight() - 2));
|
||||
assertRGBEquals(0xfefefd, image.getRGB(0, image.getHeight() - 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSubsamplingBounds1026() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/read-error1026.jpg")));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceSubsampling(3, 3, 1, 1);
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
|
||||
// Make sure correct color is actually read, not just left empty
|
||||
assertRGBEquals(0xfefefd, image.getRGB(0, image.getHeight() - 2));
|
||||
assertRGBEquals(0xfefefd, image.getRGB(0, image.getHeight() - 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSubsamplingBounds1025() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/read-error1025.jpg")));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceSubsampling(3, 3, 1, 1);
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testReadSubsamplingNotSkippingLines1025() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/read-error1025.jpg")));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceSubsampling(3, 3, 1, 1);
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
|
||||
// Make sure correct color is actually read, not just left empty
|
||||
assertRGBEquals(0xfefefd, image.getRGB(0, image.getHeight() - 2));
|
||||
assertRGBEquals(0xfefefd, image.getRGB(0, image.getHeight() - 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSubsamplingBounds1024() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/read-error1024.jpg")));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceSubsampling(3, 3, 1, 1);
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
|
||||
// Make sure correct color is actually read, not just left empty
|
||||
assertRGBEquals(0xfefefd, image.getRGB(0, image.getHeight() - 2));
|
||||
assertRGBEquals(0xfefefd, image.getRGB(0, image.getHeight() - 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXDensityOutOfRangeIssue() throws IOException {
|
||||
// Image has JFIF with x/y density 0
|
||||
@@ -820,6 +966,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
|
||||
if (expectedTree == null) {
|
||||
assertNull(actualTree);
|
||||
return;
|
||||
}
|
||||
|
||||
assertEquals(String.format("%s: Node names differ", message), expectedTree.getNodeName(), actualTree.getNodeName());
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
|
||||
+10
@@ -120,6 +120,16 @@ final class EXIFEntry extends AbstractEntry {
|
||||
return "Artist";
|
||||
case TIFF.TAG_HOST_COMPUTER:
|
||||
return "HostComputer";
|
||||
case TIFF.TAG_PREDICTOR:
|
||||
return "Predictor";
|
||||
case TIFF.TAG_TILE_WIDTH:
|
||||
return "TileWidth";
|
||||
case TIFF.TAG_TILE_HEIGTH:
|
||||
return "TileHeight";
|
||||
case TIFF.TAG_TILE_OFFSETS:
|
||||
return "TileOffsets";
|
||||
case TIFF.TAG_TILE_BYTE_COUNTS:
|
||||
return "TileByteCounts";
|
||||
case TIFF.TAG_COPYRIGHT:
|
||||
return "Copyright";
|
||||
case TIFF.TAG_YCBCR_SUB_SAMPLING:
|
||||
|
||||
+3
@@ -143,6 +143,9 @@ public interface TIFF {
|
||||
int TAG_WHITE_POINT = 318;
|
||||
int TAG_PRIMARY_CHROMATICITIES = 319;
|
||||
int TAG_COLOR_MAP = 320;
|
||||
int TAG_INK_SET = 332;
|
||||
int TAG_INK_NAMES = 333;
|
||||
int TAG_NUMBER_OF_INKS = 334;
|
||||
int TAG_EXTRA_SAMPLES = 338;
|
||||
int TAG_TRANSFER_RANGE = 342;
|
||||
int TAG_YCBCR_COEFFICIENTS = 529;
|
||||
|
||||
+20
-13
@@ -29,7 +29,9 @@
|
||||
package com.twelvemonkeys.imageio.metadata.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.exif.EXIFReader;
|
||||
import com.twelvemonkeys.imageio.metadata.psd.PSD;
|
||||
import com.twelvemonkeys.imageio.metadata.psd.PSDReader;
|
||||
import com.twelvemonkeys.imageio.metadata.xmp.XMP;
|
||||
import com.twelvemonkeys.imageio.metadata.xmp.XMPReader;
|
||||
@@ -38,6 +40,8 @@ import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
@@ -155,23 +159,21 @@ public final class JPEGSegmentUtil {
|
||||
}
|
||||
|
||||
static JPEGSegment readSegment(final ImageInputStream stream, final Map<Integer, List<String>> segmentIdentifiers) throws IOException {
|
||||
int marker = stream.readUnsignedShort();
|
||||
// int trash = 0;
|
||||
int marker = stream.readUnsignedByte();
|
||||
|
||||
// Skip over weird 0x00 padding...?
|
||||
int bad = 0;
|
||||
while (marker == 0) {
|
||||
marker = stream.readUnsignedShort();
|
||||
bad += 2;
|
||||
// Skip trash padding before the marker
|
||||
while (marker != 0xff) {
|
||||
marker = stream.readUnsignedByte();
|
||||
// trash++;
|
||||
}
|
||||
|
||||
if (marker == 0x00ff) {
|
||||
bad++;
|
||||
marker = 0xff00 | stream.readUnsignedByte();
|
||||
}
|
||||
// if (trash != 0) {
|
||||
// TODO: Issue warning?
|
||||
// System.err.println("trash: " + trash);
|
||||
// }
|
||||
|
||||
if (bad != 0) {
|
||||
// System.err.println("bad: " + bad);
|
||||
}
|
||||
marker = 0xff00 | stream.readUnsignedByte();
|
||||
|
||||
// Skip over 0xff padding between markers
|
||||
while (marker == 0xffff) {
|
||||
@@ -290,6 +292,11 @@ public final class JPEGSegmentUtil {
|
||||
// IPTC metadata. Probably duplicated in the XMP though...
|
||||
ImageInputStream stream = new ByteArrayImageInputStream(segment.data, segment.offset(), segment.length());
|
||||
Directory psd = new PSDReader().read(stream);
|
||||
Entry iccEntry = psd.getEntryById(PSD.RES_ICC_PROFILE);
|
||||
if (iccEntry != null) {
|
||||
ICC_ColorSpace colorSpace = new ICC_ColorSpace(ICC_Profile.getInstance((byte[]) iccEntry.getValue()));
|
||||
System.err.println("colorSpace: " + colorSpace);
|
||||
}
|
||||
System.err.println("PSD: " + psd);
|
||||
System.err.println(EXIFReader.HexDump.dump(segment.data));
|
||||
}
|
||||
|
||||
Regular → Executable
+8
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -36,7 +36,12 @@ package com.twelvemonkeys.imageio.metadata.psd;
|
||||
* @version $Id: PSD.java,v 1.0 24.01.12 16:51 haraldk Exp$
|
||||
*/
|
||||
public interface PSD {
|
||||
static final int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M';
|
||||
/** PSD image resource marker "8BIM". */
|
||||
int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M';
|
||||
|
||||
static final int RES_IPTC_NAA = 0x0404;
|
||||
/** IPTC image resource id. */
|
||||
int RES_IPTC_NAA = 0x0404;
|
||||
|
||||
/** ICC profile image resource id. */
|
||||
int RES_ICC_PROFILE = 0x040f;
|
||||
}
|
||||
|
||||
Regular → Executable
+5
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -136,6 +136,10 @@ public final class PSDReader extends MetadataReader {
|
||||
pInput.readFully(data);
|
||||
}
|
||||
|
||||
public final int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public final byte[] data() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pdf</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pict</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
|
||||
|
||||
+7
-2
@@ -2623,8 +2623,13 @@ public class PICTImageReader extends ImageReaderBase {
|
||||
Graphics2D g = image.createGraphics();
|
||||
try {
|
||||
// TODO: Might need to clear background
|
||||
|
||||
g.setTransform(AffineTransform.getScaleInstance(screenImageXRatio / subX, screenImageYRatio / subY));
|
||||
AffineTransform instance = new AffineTransform();
|
||||
if (pParam != null && pParam.getSourceRegion() != null) {
|
||||
Rectangle rectangle = pParam.getSourceRegion();
|
||||
instance.translate(-rectangle.x, -rectangle.y);
|
||||
}
|
||||
instance.scale(screenImageXRatio / subX, screenImageYRatio / subY);
|
||||
g.setTransform(instance);
|
||||
// try {
|
||||
drawOnto(g);
|
||||
// }
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-psd</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PSD plugin</name>
|
||||
|
||||
+28
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -42,7 +42,7 @@ import java.io.InputStream;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: ICCProfile.java,v 1.0 May 20, 2008 6:24:10 PM haraldk Exp$
|
||||
*/
|
||||
class ICCProfile extends PSDImageResource {
|
||||
final class ICCProfile extends PSDImageResource {
|
||||
private ICC_Profile profile;
|
||||
|
||||
ICCProfile(final short pId, final ImageInputStream pInput) throws IOException {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -35,15 +35,20 @@ package com.twelvemonkeys.imageio.plugins.psd;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSD.java,v 1.0 Apr 29, 2008 4:47:47 PM haraldk Exp$
|
||||
*
|
||||
* @see <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml">Adobe Photoshop File Formats Specification</a>
|
||||
* @see <a href="http://www.fileformat.info/format/psd/egff.htm">http://www.fileformat.info/format/psd/egff.htm</a>
|
||||
*/
|
||||
interface PSD {
|
||||
/** PSD 2+ Native format (.PSD) identifier "8BPS" */
|
||||
int SIGNATURE_8BPS = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'S';
|
||||
|
||||
// TODO: Is this ever used??! Spec says (and sample files uses) 8BPS + version == 2 for PSB...
|
||||
/** PSD 5+ Large Document Format (.PSB) identifier "8BPB" */
|
||||
int SIGNATURE_8BPB = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'B';
|
||||
|
||||
int VERSION_PSD = 1;
|
||||
int VERSION_PSB = 2;
|
||||
|
||||
/** PSD Resource type identifier "8BIM" */
|
||||
int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M';
|
||||
|
||||
@@ -105,7 +110,7 @@ interface PSD {
|
||||
|
||||
// Color Modes
|
||||
/** Bitmap (monochrome) */
|
||||
short COLOR_MODE_MONOCHROME = 0;
|
||||
short COLOR_MODE_BITMAP = 0;
|
||||
|
||||
/** Gray-scale */
|
||||
short COLOR_MODE_GRAYSCALE = 1;
|
||||
@@ -541,4 +546,13 @@ interface PSD {
|
||||
*/
|
||||
int RES_PRINT_FLAGS_INFORMATION = 0x2710;
|
||||
|
||||
int RES_PATH_INFO_MAX = 0x0bb6;
|
||||
int RES_PATH_INFO_MIN = 0x07d0;
|
||||
|
||||
/** Plug-In resource(s). Resources added by a plug-in. See the plug-in API found in the SDK documentation */
|
||||
int RES_PLUGIN_MIN = 0x0fa0;
|
||||
|
||||
/** Plug-In resource(s). Resources added by a plug-in. See the plug-in API found in the SDK documentation */
|
||||
int RES_PLUGIN_MAX = 0x1387;
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -40,7 +40,7 @@ import java.util.List;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDAlphaChannelInfo.java,v 1.0 May 2, 2008 5:33:40 PM haraldk Exp$
|
||||
*/
|
||||
class PSDAlphaChannelInfo extends PSDImageResource {
|
||||
final class PSDAlphaChannelInfo extends PSDImageResource {
|
||||
List<String> names;
|
||||
|
||||
public PSDAlphaChannelInfo(short pId, final ImageInputStream pInput) throws IOException {
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -35,7 +35,7 @@ package com.twelvemonkeys.imageio.plugins.psd;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDChannelInfo.java,v 1.0 May 6, 2008 2:46:23 PM haraldk Exp$
|
||||
*/
|
||||
class PSDChannelInfo {
|
||||
final class PSDChannelInfo {
|
||||
final short channelId;
|
||||
final long length;
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -38,7 +38,7 @@ import java.io.IOException;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDChannelSourceDestinationRange.java,v 1.0 May 6, 2008 5:14:13 PM haraldk Exp$
|
||||
*/
|
||||
class PSDChannelSourceDestinationRange {
|
||||
final class PSDChannelSourceDestinationRange {
|
||||
private String channel;
|
||||
private short sourceBlack;
|
||||
private short sourceWhite;
|
||||
|
||||
Regular → Executable
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -43,7 +43,7 @@ import java.io.IOException;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDColorData.java,v 1.0 Apr 29, 2008 5:33:01 PM haraldk Exp$
|
||||
*/
|
||||
class PSDColorData {
|
||||
final class PSDColorData {
|
||||
final byte[] colors;
|
||||
private IndexColorModel colorModel;
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -39,7 +39,7 @@ import java.io.IOException;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$
|
||||
*/
|
||||
class PSDDisplayInfo extends PSDImageResource {
|
||||
final class PSDDisplayInfo extends PSDImageResource {
|
||||
// TODO: Size of this struct should be 14.. Does not compute... Something bogus here
|
||||
|
||||
// ColorSpace definitions:
|
||||
|
||||
+28
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -38,8 +38,8 @@ import java.io.IOException;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDGlobalLayerMask.java,v 1.0 May 8, 2008 5:33:48 PM haraldk Exp$
|
||||
*/
|
||||
class PSDGlobalLayerMask {
|
||||
final int colorSpace;
|
||||
final class PSDGlobalLayerMask {
|
||||
final int colorSpace;
|
||||
final int color1;
|
||||
final int color2;
|
||||
final int color3;
|
||||
|
||||
+28
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
Regular → Executable
+97
-55
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -28,8 +28,9 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.psd;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.IIOException;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
@@ -39,19 +40,21 @@ import java.io.IOException;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDHeader.java,v 1.0 Apr 29, 2008 5:18:22 PM haraldk Exp$
|
||||
*/
|
||||
class PSDHeader {
|
||||
// The header is 26 bytes in length and is structured as follows:
|
||||
final class PSDHeader {
|
||||
static final int PSD_MAX_SIZE = 30000;
|
||||
static final int PSB_MAX_SIZE = 300000;
|
||||
// The header is 26 bytes in length and is structured as follows:
|
||||
//
|
||||
// typedef struct _PSD_HEADER
|
||||
// {
|
||||
// BYTE Signature[4]; /* File ID "8BPS" */
|
||||
// WORD Version; /* Version number, always 1 */
|
||||
// WORD Version; /* Version number, always 1. 2 for PSB */
|
||||
// BYTE Reserved[6]; /* Reserved, must be zeroed */
|
||||
// WORD Channels; /* Number of color channels (1-24) including alpha
|
||||
// WORD Channels; /* Number of color channels (1-56) including alpha
|
||||
// channels */
|
||||
// LONG Rows; /* Height of image in pixels (1-30000) */
|
||||
// LONG Columns; /* Width of image in pixels (1-30000) */
|
||||
// WORD Depth; /* Number of bits per channel (1, 8, and 16) */
|
||||
// LONG Rows; /* Height of image in pixels (1-30000/1-300000 for PSB) */
|
||||
// LONG Columns; /* Width of image in pixels (1-30000/1-300000 for PSB) */
|
||||
// WORD Depth; /* Number of bits per channel (1, 8, 16 or 32) */
|
||||
// WORD Mode; /* Color mode */
|
||||
// } PSD_HEADER;
|
||||
|
||||
@@ -60,8 +63,9 @@ class PSDHeader {
|
||||
final int height;
|
||||
final short bits;
|
||||
final short mode;
|
||||
final boolean largeFormat;
|
||||
|
||||
PSDHeader(final ImageInputStream pInput) throws IOException {
|
||||
PSDHeader(final DataInput pInput) throws IOException {
|
||||
int signature = pInput.readInt();
|
||||
if (signature != PSD.SIGNATURE_8BPS) {
|
||||
throw new IIOException("Not a PSD document, expected signature \"8BPS\": \"" + PSDUtil.intToStr(signature) + "\" (0x" + Integer.toHexString(signature) + ")");
|
||||
@@ -70,67 +74,105 @@ class PSDHeader {
|
||||
int version = pInput.readUnsignedShort();
|
||||
|
||||
switch (version) {
|
||||
case 1:
|
||||
case PSD.VERSION_PSD:
|
||||
largeFormat = false;
|
||||
break;
|
||||
case PSD.VERSION_PSB:
|
||||
largeFormat = true;
|
||||
break;
|
||||
case 2:
|
||||
throw new IIOException("Photoshop Large Document Format (PSB) not supported yet.");
|
||||
default:
|
||||
throw new IIOException(String.format("Unknown PSD version, expected 1 or 2: 0x%08x", version));
|
||||
}
|
||||
|
||||
byte[] reserved = new byte[6];
|
||||
pInput.readFully(reserved);
|
||||
pInput.readFully(reserved); // We don't really care
|
||||
|
||||
channels = pInput.readShort();
|
||||
if (channels <= 0) {
|
||||
throw new IIOException(String.format("Unsupported number of channels: %d", channels));
|
||||
}
|
||||
|
||||
height = pInput.readInt(); // Rows
|
||||
width = pInput.readInt(); // Columns
|
||||
|
||||
bits = pInput.readShort();
|
||||
|
||||
switch (bits) {
|
||||
case 1:
|
||||
case 8:
|
||||
case 16:
|
||||
case 32:
|
||||
break;
|
||||
default:
|
||||
throw new IIOException(String.format("Unsupported bit depth for PSD: %d bits", bits));
|
||||
}
|
||||
|
||||
mode = pInput.readShort();
|
||||
|
||||
switch (mode) {
|
||||
case PSD.COLOR_MODE_BITMAP:
|
||||
case PSD.COLOR_MODE_GRAYSCALE:
|
||||
case PSD.COLOR_MODE_INDEXED:
|
||||
case PSD.COLOR_MODE_RGB:
|
||||
case PSD.COLOR_MODE_CMYK:
|
||||
case PSD.COLOR_MODE_MULTICHANNEL:
|
||||
case PSD.COLOR_MODE_DUOTONE:
|
||||
case PSD.COLOR_MODE_LAB:
|
||||
break;
|
||||
default:
|
||||
throw new IIOException(String.format("Unsupported mode depth for PSD: %d", mode));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
|
||||
builder.append("[Channels: ");
|
||||
builder.append(channels);
|
||||
builder.append(", width: ");
|
||||
builder.append(width);
|
||||
builder.append(", height: ");
|
||||
builder.append(height);
|
||||
builder.append(", depth: ");
|
||||
builder.append(bits);
|
||||
builder.append(", mode: ");
|
||||
builder.append(mode);
|
||||
switch (mode) {
|
||||
case PSD.COLOR_MODE_MONOCHROME:
|
||||
builder.append(" (Monochrome)");
|
||||
break;
|
||||
case PSD.COLOR_MODE_GRAYSCALE:
|
||||
builder.append(" (Grayscale)");
|
||||
break;
|
||||
case PSD.COLOR_MODE_INDEXED:
|
||||
builder.append(" (Indexed)");
|
||||
break;
|
||||
case PSD.COLOR_MODE_RGB:
|
||||
builder.append(" (RGB)");
|
||||
break;
|
||||
case PSD.COLOR_MODE_CMYK:
|
||||
builder.append(" (CMYK)");
|
||||
break;
|
||||
case PSD.COLOR_MODE_MULTICHANNEL:
|
||||
builder.append(" (Multi channel)");
|
||||
break;
|
||||
case PSD.COLOR_MODE_DUOTONE:
|
||||
builder.append(" (Duotone)");
|
||||
break;
|
||||
case PSD.COLOR_MODE_LAB:
|
||||
builder.append(" (Lab color)");
|
||||
break;
|
||||
default:
|
||||
builder.append(" (Unkown mode)");
|
||||
}
|
||||
builder.append("]");
|
||||
return new StringBuilder(getClass().getSimpleName())
|
||||
.append("[version: ")
|
||||
.append(largeFormat ? "2" : "1")
|
||||
.append(", channels: ")
|
||||
.append(channels)
|
||||
.append(", width: ")
|
||||
.append(width)
|
||||
.append(", height: ")
|
||||
.append(height)
|
||||
.append(", depth: ")
|
||||
.append(bits)
|
||||
.append(", mode: ")
|
||||
.append(mode)
|
||||
.append(" (")
|
||||
.append(modeAsString())
|
||||
.append(")]")
|
||||
.toString();
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
int getMaxSize() {
|
||||
return largeFormat ? PSB_MAX_SIZE : PSD_MAX_SIZE;
|
||||
}
|
||||
|
||||
boolean hasValidDimensions() {
|
||||
return width <= getMaxSize() && height <= getMaxSize();
|
||||
}
|
||||
|
||||
private String modeAsString() {
|
||||
switch (mode) {
|
||||
case PSD.COLOR_MODE_BITMAP:
|
||||
return "Monochrome";
|
||||
case PSD.COLOR_MODE_GRAYSCALE:
|
||||
return "Grayscale";
|
||||
case PSD.COLOR_MODE_INDEXED:
|
||||
return "Indexed";
|
||||
case PSD.COLOR_MODE_RGB:
|
||||
return "RGB";
|
||||
case PSD.COLOR_MODE_CMYK:
|
||||
return "CMYK";
|
||||
case PSD.COLOR_MODE_MULTICHANNEL:
|
||||
return "Multi channel";
|
||||
case PSD.COLOR_MODE_DUOTONE:
|
||||
return "Duotone";
|
||||
case PSD.COLOR_MODE_LAB:
|
||||
return "Lab color";
|
||||
default:
|
||||
return "Unkown mode";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
|
||||
Regular → Executable
+313
-230
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -32,15 +32,12 @@ import com.twelvemonkeys.image.ImageUtil;
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
@@ -70,12 +67,12 @@ import java.util.List;
|
||||
// See http://www.codeproject.com/KB/graphics/PSDParser.aspx
|
||||
// See http://www.adobeforums.com/webx?14@@.3bc381dc/0
|
||||
// Done: Allow reading the extra alpha channels (index after composite data)
|
||||
public class PSDImageReader extends ImageReaderBase {
|
||||
public final class PSDImageReader extends ImageReaderBase {
|
||||
private PSDHeader header;
|
||||
private ICC_ColorSpace colorSpace;
|
||||
protected PSDMetadata metadata;
|
||||
private PSDMetadata metadata;
|
||||
|
||||
protected PSDImageReader(final ImageReaderSpi originatingProvider) {
|
||||
PSDImageReader(final ImageReaderSpi originatingProvider) {
|
||||
super(originatingProvider);
|
||||
}
|
||||
|
||||
@@ -108,10 +105,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
private int getLayerWidth(int layerIndex) throws IOException {
|
||||
if (metadata == null || metadata.layerInfo == null) {
|
||||
readImageResources(false);
|
||||
readLayerAndMaskInfo(true);
|
||||
}
|
||||
readLayerAndMaskInfo(true);
|
||||
|
||||
PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex);
|
||||
|
||||
@@ -119,10 +113,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
private int getLayerHeight(int layerIndex) throws IOException {
|
||||
if (metadata == null || metadata.layerInfo == null) {
|
||||
readImageResources(false);
|
||||
readLayerAndMaskInfo(true);
|
||||
}
|
||||
readLayerAndMaskInfo(true);
|
||||
|
||||
PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex);
|
||||
|
||||
@@ -136,14 +127,10 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
private ImageTypeSpecifier getRawImageTypeInternal(final int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
// Image index above 0, means a layer
|
||||
if (imageIndex > 0) {
|
||||
if (metadata == null || metadata.layerInfo == null) {
|
||||
readImageResources(false);
|
||||
readLayerAndMaskInfo(true);
|
||||
}
|
||||
readLayerAndMaskInfo(true);
|
||||
|
||||
return getRawImageTypeForLayer(imageIndex - 1);
|
||||
}
|
||||
@@ -154,46 +141,56 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
private ImageTypeSpecifier getRawImageTypeForCompositeLayer() throws IOException {
|
||||
ColorSpace cs;
|
||||
|
||||
switch (header.mode) {
|
||||
case PSD.COLOR_MODE_MONOCHROME:
|
||||
case PSD.COLOR_MODE_BITMAP:
|
||||
if (header.channels == 1 && header.bits == 1) {
|
||||
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY);
|
||||
}
|
||||
|
||||
throw new IIOException(
|
||||
String.format("Unsupported channel count/bit depth for Monochrome PSD: %d channels/%d bits", header.channels, header.bits)
|
||||
);
|
||||
throw new IIOException(String.format("Unsupported channel count/bit depth for Monochrome PSD: %d channels/%d bits", header.channels, header.bits));
|
||||
|
||||
case PSD.COLOR_MODE_INDEXED:
|
||||
// TODO: 16 bit indexed?! Does it exist?
|
||||
if (header.channels == 1 && header.bits == 8) {
|
||||
return IndexedImageTypeSpecifier.createFromIndexColorModel(metadata.colorData.getIndexColorModel());
|
||||
}
|
||||
|
||||
throw new IIOException(
|
||||
String.format("Unsupported channel count/bit depth for Indexed Color PSD: %d channels/%d bits", header.channels, header.bits)
|
||||
);
|
||||
throw new IIOException(String.format("Unsupported channel count/bit depth for Indexed Color PSD: %d channels/%d bits", header.channels, header.bits));
|
||||
|
||||
case PSD.COLOR_MODE_DUOTONE:
|
||||
// NOTE: Duotone (whatever that is) should be treated as gray scale
|
||||
// Fall-through
|
||||
case PSD.COLOR_MODE_GRAYSCALE:
|
||||
cs = getEmbeddedColorSpace();
|
||||
if (cs == null) {
|
||||
cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
|
||||
}
|
||||
|
||||
if (header.channels == 1 && header.bits == 8) {
|
||||
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
|
||||
}
|
||||
else if (header.channels == 2 && header.bits == 8) {
|
||||
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, DataBuffer.TYPE_BYTE, true, false);
|
||||
}
|
||||
else if (header.channels == 1 && header.bits == 16) {
|
||||
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_USHORT_GRAY);
|
||||
}
|
||||
else if (header.channels == 2 && header.bits == 16) {
|
||||
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, DataBuffer.TYPE_USHORT, true, false);
|
||||
}
|
||||
else if (header.channels == 1 && header.bits == 32) {
|
||||
return ImageTypeSpecifier.createBanded(cs, new int[] {0}, new int[] {0}, DataBuffer.TYPE_INT, false, false);
|
||||
}
|
||||
else if (header.channels == 2 && header.bits == 32) {
|
||||
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, DataBuffer.TYPE_INT, true, false);
|
||||
}
|
||||
|
||||
throw new IIOException(
|
||||
String.format("Unsupported channel count/bit depth for Gray Scale PSD: %d channels/%d bits", header.channels, header.bits)
|
||||
);
|
||||
throw new IIOException(String.format("Unsupported channel count/bit depth for Gray Scale PSD: %d channels/%d bits", header.channels, header.bits));
|
||||
|
||||
case PSD.COLOR_MODE_RGB:
|
||||
cs = getEmbeddedColorSpace();
|
||||
if (cs == null) {
|
||||
// TODO: Should probably be Adobe RGB (1998), not sRGB. Or..? Can't find any spec saying either...
|
||||
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); // ColorSpaces.getColorSpace(ColorSpaces.CS_ADOBE_RGB_1998); ?
|
||||
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
||||
}
|
||||
|
||||
if (header.channels == 3 && header.bits == 8) {
|
||||
@@ -208,10 +205,14 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
else if (header.channels >= 4 && header.bits == 16) {
|
||||
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_USHORT, true, false);
|
||||
}
|
||||
else if (header.channels == 3 && header.bits == 32) {
|
||||
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2}, new int[] {0, 0, 0}, DataBuffer.TYPE_INT, false, false);
|
||||
}
|
||||
else if (header.channels >= 4 && header.bits == 32) {
|
||||
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_INT, true, false);
|
||||
}
|
||||
|
||||
throw new IIOException(
|
||||
String.format("Unsupported channel count/bit depth for RGB PSD: %d channels/%d bits", header.channels, header.bits)
|
||||
);
|
||||
throw new IIOException(String.format("Unsupported channel count/bit depth for RGB PSD: %d channels/%d bits", header.channels, header.bits));
|
||||
|
||||
case PSD.COLOR_MODE_CMYK:
|
||||
cs = getEmbeddedColorSpace();
|
||||
@@ -231,10 +232,14 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
else if (header.channels == 5 && header.bits == 16) {
|
||||
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2, 3, 4}, new int[] {0, 0, 0, 0, 0}, DataBuffer.TYPE_USHORT, true, false);
|
||||
}
|
||||
else if (header.channels == 4 && header.bits == 32) {
|
||||
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_INT, false, false);
|
||||
}
|
||||
else if (header.channels == 5 && header.bits == 32) {
|
||||
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2, 3, 4}, new int[] {0, 0, 0, 0, 0}, DataBuffer.TYPE_INT, true, false);
|
||||
}
|
||||
|
||||
throw new IIOException(
|
||||
String.format("Unsupported channel count/bit depth for CMYK PSD: %d channels/%d bits", header.channels, header.bits)
|
||||
);
|
||||
throw new IIOException(String.format("Unsupported channel count/bit depth for CMYK PSD: %d channels/%d bits", header.channels, header.bits));
|
||||
|
||||
case PSD.COLOR_MODE_MULTICHANNEL:
|
||||
// TODO: Implement
|
||||
@@ -242,9 +247,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// TODO: Implement
|
||||
// TODO: If there's a color profile embedded, it should be easy, otherwise we're out of luck...
|
||||
default:
|
||||
throw new IIOException(
|
||||
String.format("Unsupported PSD MODE: %s (%d channels/%d bits)", header.mode, header.channels, header.bits)
|
||||
);
|
||||
throw new IIOException(String.format("Unsupported PSD MODE: %s (%d channels/%d bits)", header.mode, header.channels, header.bits));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,8 +333,6 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
private ColorSpace getEmbeddedColorSpace() throws IOException {
|
||||
readImageResources(true);
|
||||
// TODO: Skip this, requires storing some stream offsets
|
||||
readLayerAndMaskInfo(false);
|
||||
|
||||
if (colorSpace == null) {
|
||||
ICC_Profile profile = null;
|
||||
@@ -351,27 +352,12 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
public BufferedImage read(final int imageIndex, final ImageReadParam param) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
|
||||
readHeader();
|
||||
|
||||
readImageResources(false);
|
||||
// readLayerAndMaskInfo(false);
|
||||
readLayerAndMaskInfo(imageIndex > 0);
|
||||
|
||||
// TODO: What about the extra alpha channels possibly present? Read as gray scale as extra images?
|
||||
|
||||
// Layer hacks... For now, any index above 0 is considered to be a layer...
|
||||
// TODO: Support layer in index 0, if "has real merged data" flag is false?
|
||||
// TODO: Param support in layer code (more duping/cleanup..)
|
||||
if (imageIndex > 0) {
|
||||
// ImageTypeSpecifier compositeType = getRawImageTypeForCompositeLayer();
|
||||
// ImageTypeSpecifier imageType = getImageTypes(0).next();
|
||||
|
||||
// int layerIndex = imageIndex - 1;
|
||||
|
||||
// PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex);
|
||||
//
|
||||
// imageInput.seek(findLayerStartPos(layerIndex));
|
||||
// return readLayerData(layerIndex, layerInfo, compositeType, imageType, param);
|
||||
return readLayerData(imageIndex - 1, param);
|
||||
}
|
||||
|
||||
@@ -421,13 +407,11 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
ySub = param.getSourceYSubsampling();
|
||||
}
|
||||
|
||||
processImageStarted(imageIndex);
|
||||
|
||||
int[] byteCounts = null;
|
||||
imageInput.seek(metadata.imageDataStart);
|
||||
int compression = imageInput.readShort();
|
||||
// TODO: Need to make sure compression is set in metadata, even without reading the image data!
|
||||
metadata.compression = compression;
|
||||
|
||||
int[] byteCounts = null;
|
||||
switch (compression) {
|
||||
case PSD.COMPRESSION_NONE:
|
||||
break;
|
||||
@@ -435,14 +419,12 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// NOTE: Byte counts will allow us to easily skip rows before AOI
|
||||
byteCounts = new int[header.channels * header.height];
|
||||
for (int i = 0; i < byteCounts.length; i++) {
|
||||
byteCounts[i] = imageInput.readUnsignedShort();
|
||||
byteCounts[i] = header.largeFormat ? imageInput.readInt() : imageInput.readUnsignedShort();
|
||||
}
|
||||
break;
|
||||
case PSD.COMPRESSION_ZIP:
|
||||
// TODO: Could probably use the ZIPDecoder (DeflateDecoder) here..
|
||||
case PSD.COMPRESSION_ZIP_PREDICTION:
|
||||
// TODO: Look at TIFF prediction reading
|
||||
// Could be same as PNG prediction? Read up...
|
||||
// TODO: Could probably use the ZIPDecoder (DeflateDecoder) here.. Look at TIFF prediction reading
|
||||
throw new IIOException("PSD with ZIP compression not supported");
|
||||
default:
|
||||
throw new IIOException(
|
||||
@@ -453,6 +435,8 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
);
|
||||
}
|
||||
|
||||
processImageStarted(imageIndex);
|
||||
|
||||
// What we read here is the "composite layer" of the PSD file
|
||||
readImageData(image, rawType.getColorModel(), source, dest, xSub, ySub, byteCounts, compression);
|
||||
|
||||
@@ -512,8 +496,12 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
|
||||
read16bitChannel(c, header.channels, raster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row16, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE);
|
||||
break;
|
||||
case 32:
|
||||
int[] row32 = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
|
||||
read32bitChannel(c, header.channels, raster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row32, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE);
|
||||
break;
|
||||
default:
|
||||
throw new IIOException(String.format("Unknown PSD bit depth: %s", header.bits));
|
||||
throw new IIOException(String.format("Unsupported PSD bit depth: %s", header.bits));
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
@@ -531,6 +519,67 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
processImageProgress(100f * channel / channelCount + 100f * y / (height * channelCount));
|
||||
}
|
||||
|
||||
private void read32bitChannel(final int pChannel, final int pChannelCount,
|
||||
final DataBuffer pData, final int pBands, final int pBandOffset,
|
||||
final ColorModel pSourceColorModel,
|
||||
final int[] pRow,
|
||||
final Rectangle pSource, final Rectangle pDest,
|
||||
final int pXSub, final int pYSub,
|
||||
final int pChannelWidth, final int pChannelHeight,
|
||||
final int[] pRowByteCounts, final int pRowOffset,
|
||||
final boolean pRLECompressed) throws IOException {
|
||||
|
||||
final boolean isCMYK = pSourceColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
|
||||
final int colorComponents = pSourceColorModel.getColorSpace().getNumComponents();
|
||||
final boolean banded = pData.getNumBanks() > 1;
|
||||
|
||||
for (int y = 0; y < pChannelHeight; y++) {
|
||||
// NOTE: Length is in *16 bit values* (shorts)
|
||||
int length = 2 * (pRLECompressed ? pRowByteCounts[pRowOffset + y] : pChannelWidth);
|
||||
|
||||
// TODO: Sometimes need to read the line y == source.y + source.height...
|
||||
// Read entire line, if within source region and sampling
|
||||
if (y >= pSource.y && y < pSource.y + pSource.height && y % pYSub == 0) {
|
||||
if (pRLECompressed) {
|
||||
DataInputStream input = PSDUtil.createPackBitsStream(imageInput, length);
|
||||
try {
|
||||
for (int x = 0; x < pChannelWidth; x++) {
|
||||
pRow[x] = input.readInt();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
else {
|
||||
imageInput.readFully(pRow, 0, pChannelWidth);
|
||||
}
|
||||
|
||||
// TODO: Destination offset...??
|
||||
// Copy line sub sampled into real data
|
||||
int offset = (y - pSource.y) / pYSub * pDest.width * pBands + pBandOffset;
|
||||
for (int x = 0; x < pDest.width; x++) {
|
||||
int value = pRow[pSource.x + x * pXSub];
|
||||
|
||||
// CMYK values are stored inverted, but alpha is not
|
||||
if (isCMYK && pChannel < colorComponents) {
|
||||
value = 0xffffffff - value;
|
||||
}
|
||||
|
||||
pData.setElem(banded ? pChannel : 0, offset + x * pBands, value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
imageInput.skipBytes(length);
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
break;
|
||||
}
|
||||
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private void read16bitChannel(final int pChannel, final int pChannelCount,
|
||||
final DataBuffer pData, final int pBands, final int pBandOffset,
|
||||
final ColorModel pSourceColorModel,
|
||||
@@ -731,7 +780,7 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
private void decomposeAlpha(final ColorModel pModel, final DataBuffer pBuffer,
|
||||
final int pWidth, final int pHeight, final int pChannels) {
|
||||
// TODO: Is the document background always white!?
|
||||
// NOTE: It seems that the document background always white..?!
|
||||
// TODO: What about CMYK + alpha?
|
||||
if (pModel.hasAlpha() && pModel.getColorSpace().getType() == ColorSpace.TYPE_RGB) {
|
||||
|
||||
@@ -791,9 +840,16 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
private void readHeader() throws IOException {
|
||||
assertInput();
|
||||
|
||||
if (header == null) {
|
||||
header = new PSDHeader(imageInput);
|
||||
|
||||
if (!header.hasValidDimensions()) {
|
||||
processWarningOccurred(String.format("Dimensions exceed maximum allowed for %s: %dx%d (max %dx%d)",
|
||||
header.largeFormat ? "PSB" : "PSD",
|
||||
header.width, header.height, header.getMaxSize(), header.getMaxSize()));
|
||||
}
|
||||
|
||||
metadata = new PSDMetadata();
|
||||
metadata.header = header;
|
||||
|
||||
@@ -818,6 +874,8 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
imageInput.skipBytes(length);
|
||||
}
|
||||
|
||||
metadata.imageResourcesStart = imageInput.getStreamPosition();
|
||||
|
||||
// Don't need the header again
|
||||
imageInput.flushBefore(imageInput.getStreamPosition());
|
||||
}
|
||||
@@ -826,85 +884,104 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
// TODO: Flags or list of interesting resources to parse
|
||||
// TODO: Obey ignoreMetadata
|
||||
private void readImageResources(final boolean pParseData) throws IOException {
|
||||
// TODO: Avoid unnecessary stream repositioning
|
||||
long pos = imageInput.getFlushedPosition();
|
||||
imageInput.seek(pos);
|
||||
readHeader();
|
||||
|
||||
long length = imageInput.readUnsignedInt();
|
||||
if (pParseData || metadata.layerAndMaskInfoStart == 0) {
|
||||
imageInput.seek(metadata.imageResourcesStart);
|
||||
|
||||
if (pParseData && length > 0) {
|
||||
if (metadata.imageResources == null) {
|
||||
metadata.imageResources = new ArrayList<PSDImageResource>();
|
||||
long expectedEnd = imageInput.getStreamPosition() + length;
|
||||
long imageResourcesLength = imageInput.readUnsignedInt();
|
||||
|
||||
while (imageInput.getStreamPosition() < expectedEnd) {
|
||||
// TODO: Have PSDImageResources defer actual parsing? (Just store stream offsets)
|
||||
PSDImageResource resource = PSDImageResource.read(imageInput);
|
||||
metadata.imageResources.add(resource);
|
||||
if (pParseData && imageResourcesLength > 0) {
|
||||
if (metadata.imageResources == null) {
|
||||
metadata.imageResources = new ArrayList<PSDImageResource>();
|
||||
long expectedEnd = imageInput.getStreamPosition() + imageResourcesLength;
|
||||
|
||||
while (imageInput.getStreamPosition() < expectedEnd) {
|
||||
PSDImageResource resource = PSDImageResource.read(imageInput);
|
||||
metadata.imageResources.add(resource);
|
||||
}
|
||||
|
||||
if (imageInput.getStreamPosition() != expectedEnd) {
|
||||
throw new IIOException("Corrupt PSD document"); // ..or maybe just a bug in the reader.. ;-)
|
||||
}
|
||||
}
|
||||
|
||||
if (imageInput.getStreamPosition() != expectedEnd) {
|
||||
throw new IIOException("Corrupt PSD document"); // ..or maybe just a bug in the reader.. ;-)
|
||||
}
|
||||
// TODO: We should now be able to flush input
|
||||
// imageInput.flushBefore(metadata.imageResourcesStart + imageResourcesLength + 4);
|
||||
}
|
||||
}
|
||||
|
||||
imageInput.seek(pos + length + 4);
|
||||
metadata.layerAndMaskInfoStart = metadata.imageResourcesStart + imageResourcesLength + 4; // + 4 for the length field itself
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Flags or list of interesting resources to parse
|
||||
// TODO: Obey ignoreMetadata
|
||||
private void readLayerAndMaskInfo(final boolean pParseData) throws IOException {
|
||||
// TODO: Make sure we are positioned correctly
|
||||
// TODO: Avoid unnecessary stream repositioning
|
||||
long length = imageInput.readUnsignedInt();
|
||||
if (pParseData && length > 0) {
|
||||
long pos = imageInput.getStreamPosition();
|
||||
readImageResources(false);
|
||||
|
||||
long read;
|
||||
if (metadata.layerInfo == null) {
|
||||
long layerInfoLength = imageInput.readUnsignedInt();
|
||||
if (pParseData || metadata.imageDataStart == 0) {
|
||||
imageInput.seek(metadata.layerAndMaskInfoStart);
|
||||
|
||||
/*
|
||||
"Layer count. If it is a negative number, its absolute value is the number of
|
||||
layers and the first alpha channel contains the transparency data for the
|
||||
merged result."
|
||||
*/
|
||||
// TODO: Figure out what the last part of that sentence means in practice...
|
||||
int layers = imageInput.readShort();
|
||||
long layerAndMaskInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt();
|
||||
|
||||
PSDLayerInfo[] layerInfos = new PSDLayerInfo[Math.abs(layers)];
|
||||
for (int i = 0; i < layerInfos.length; i++) {
|
||||
layerInfos[i] = new PSDLayerInfo(imageInput);
|
||||
// NOTE: The spec says that if this section is empty, the length should be 0.
|
||||
// Yet I have a PSB file that has size 12, and both contained lengths set to 0 (which
|
||||
// is alo not as per spec, as layer count should be included if there's a layer info
|
||||
// block, so minimum size should be either 0 or 14 (or 16 if multiple of 4 for PSB))...
|
||||
|
||||
if (pParseData && layerAndMaskInfoLength > 0) {
|
||||
long pos = imageInput.getStreamPosition();
|
||||
|
||||
if (metadata.layerInfo == null) {
|
||||
long layerInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt();
|
||||
|
||||
if (layerInfoLength > 0) {
|
||||
/*
|
||||
"Layer count. If it is a negative number, its absolute value is the number of
|
||||
layers and the first alpha channel contains the transparency data for the
|
||||
merged result."
|
||||
*/
|
||||
int layerCount = imageInput.readShort();
|
||||
|
||||
PSDLayerInfo[] layerInfos = new PSDLayerInfo[Math.abs(layerCount)];
|
||||
for (int i = 0; i < layerInfos.length; i++) {
|
||||
layerInfos[i] = new PSDLayerInfo(header.largeFormat, imageInput);
|
||||
}
|
||||
metadata.layerInfo = Arrays.asList(layerInfos);
|
||||
metadata.layersStart = imageInput.getStreamPosition();
|
||||
|
||||
long read = imageInput.getStreamPosition() - pos;
|
||||
|
||||
long diff = layerInfoLength - (read - (header.largeFormat ? 8 : 4)); // - 4 for the layerInfoLength field itself
|
||||
|
||||
imageInput.skipBytes(diff);
|
||||
} else {
|
||||
metadata.layerInfo = Collections.emptyList();
|
||||
}
|
||||
|
||||
// Global LayerMaskInfo (18 bytes or more..?)
|
||||
// 4 (length), 2 (colorSpace), 8 (4 * 2 byte color components), 2 (opacity %), 1 (kind), variable (pad)
|
||||
long layerMaskInfoLength = imageInput.readUnsignedInt(); // NOTE: Not long for PSB!
|
||||
|
||||
if (layerMaskInfoLength > 0) {
|
||||
metadata.globalLayerMask = new PSDGlobalLayerMask(imageInput);
|
||||
}
|
||||
|
||||
// TODO: Parse "Additional layer information"
|
||||
|
||||
// TODO: We should now be able to flush input
|
||||
// imageInput.seek(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4));
|
||||
// imageInput.flushBefore(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4));
|
||||
}
|
||||
metadata.layerInfo = Arrays.asList(layerInfos);
|
||||
metadata.layersStart = imageInput.getStreamPosition();
|
||||
|
||||
read = imageInput.getStreamPosition() - pos;
|
||||
|
||||
long diff = layerInfoLength - (read - 4); // - 4 for the layerInfoLength field itself
|
||||
// System.out.println("diff: " + diff);
|
||||
imageInput.skipBytes(diff);
|
||||
|
||||
// TODO: Global LayerMaskInfo (18 bytes or more..?)
|
||||
// 4 (length), 2 (colorSpace), 8 (4 * 2 byte color components), 2 (opacity %), 1 (kind), variable (pad)
|
||||
long layerMaskInfoLength = imageInput.readUnsignedInt();
|
||||
// System.out.println("GlobalLayerMaskInfo length: " + layerMaskInfoLength);
|
||||
if (layerMaskInfoLength > 0) {
|
||||
metadata.globalLayerMask = new PSDGlobalLayerMask(imageInput);
|
||||
// System.err.println("globalLayerMask: " + metadata.globalLayerMask);
|
||||
}
|
||||
// read = imageInput.getStreamPosition() - pos;
|
||||
//
|
||||
// long toSkip = layerAndMaskInfoLength - read;
|
||||
// System.out.println("toSkip: " + toSkip);
|
||||
// imageInput.skipBytes(toSkip);
|
||||
}
|
||||
|
||||
read = imageInput.getStreamPosition() - pos;
|
||||
|
||||
long toSkip = length - read;
|
||||
// System.out.println("toSkip: " + toSkip);
|
||||
imageInput.skipBytes(toSkip);
|
||||
}
|
||||
else {
|
||||
// Skip entire layer and mask section
|
||||
imageInput.skipBytes(length);
|
||||
metadata.imageDataStart = metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -949,19 +1026,22 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
final boolean banded = raster.getDataBuffer().getNumBanks() > 1;
|
||||
final int interleavedBands = banded ? 1 : raster.getNumBands();
|
||||
|
||||
// TODO: progress for layers!
|
||||
// TODO: Consider creating a method in PSDLayerInfo that can tell how many channels we really want to decode
|
||||
for (PSDChannelInfo channelInfo : layerInfo.channelInfo) {
|
||||
int compression = imageInput.readUnsignedShort();
|
||||
|
||||
// Skip layer if we can't read it
|
||||
// channelId == -2 means "user supplied layer mask", whatever that is...
|
||||
if (width <= 0 || height <= 0 || channelInfo.channelId == -2 ||
|
||||
(compression != PSD.COMPRESSION_NONE && compression != PSD.COMPRESSION_RLE)) {
|
||||
// channelId
|
||||
// -1 = transparency mask; -2 = user supplied layer mask, -3 = real user supplied layer mask (when both a user mask and a vector mask are present)
|
||||
if (width <= 0 || height <= 0 || channelInfo.channelId < -1 ||
|
||||
(compression != PSD.COMPRESSION_NONE && compression != PSD.COMPRESSION_RLE)) { // TODO: ZIP Compressions!
|
||||
imageInput.skipBytes(channelInfo.length - 2);
|
||||
}
|
||||
else {
|
||||
// 0 = red, 1 = green, etc
|
||||
// -1 = transparency mask; -2 = user supplied layer mask
|
||||
int c = channelInfo.channelId == -1 ? layerInfo.channelInfo.length - 1 : channelInfo.channelId;
|
||||
// -1 = transparency mask; -2 = user supplied layer mask, -3 = real user supplied layer mask (when both a user mask and a vector mask are present)
|
||||
int c = channelInfo.channelId == -1 ? rowRaster.getNumBands() - 1 : channelInfo.channelId;
|
||||
|
||||
// NOTE: For layers, byte counts are written per channel, while for the composite data
|
||||
// byte counts are written for all channels before the image data.
|
||||
@@ -975,10 +1055,10 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
case PSD.COMPRESSION_RLE:
|
||||
// If RLE, the the image data starts with the byte counts
|
||||
// for all the scan lines in the channel (LayerBottom-LayerTop), with
|
||||
// each count stored as a two*byte value.
|
||||
// each count stored as a two*byte (four for PSB) value.
|
||||
byteCounts = new int[layerInfo.bottom - layerInfo.top];
|
||||
for (int i = 0; i < byteCounts.length; i++) {
|
||||
byteCounts[i] = imageInput.readUnsignedShort();
|
||||
byteCounts[i] = header.largeFormat ? imageInput.readInt() : imageInput.readUnsignedShort();
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -998,11 +1078,18 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
break;
|
||||
case 8:
|
||||
byte[] row8 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||
read8bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row8, area, area, xsub, ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
|
||||
read8bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row8, area, area, xsub,
|
||||
ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
|
||||
break;
|
||||
case 16:
|
||||
short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
|
||||
read16bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row16, area, area, xsub, ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
|
||||
read16bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row16, area, area, xsub,
|
||||
ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
|
||||
break;
|
||||
case 32:
|
||||
int[] row32 = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
|
||||
read32bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row32, area, area, xsub,
|
||||
ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
|
||||
break;
|
||||
default:
|
||||
throw new IIOException(String.format("Unknown PSD bit depth: %s", header.bits));
|
||||
@@ -1024,16 +1111,16 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
// If layer has more channels than composite data, it's normally extra alpha...
|
||||
if (layerInfo.channelInfo.length > compositeType.getNumBands()) {
|
||||
// ...but, it could also be just the user mask...
|
||||
boolean userMask = false;
|
||||
// ...but, it could also be just one of the user masks...
|
||||
int newBandNum = 0;
|
||||
|
||||
for (PSDChannelInfo channelInfo : layerInfo.channelInfo) {
|
||||
if (channelInfo.channelId == -2) {
|
||||
userMask = true;
|
||||
break;
|
||||
// -2 = user supplied layer mask, -3 real user supplied layer mask (when both a user mask and a vector mask are present)
|
||||
if (channelInfo.channelId >= -1) {
|
||||
newBandNum++;
|
||||
}
|
||||
}
|
||||
|
||||
int newBandNum = layerInfo.channelInfo.length - (userMask ? 1 : 0);
|
||||
|
||||
// If there really is more channels, then create new imageTypeSpec
|
||||
if (newBandNum > compositeType.getNumBands()) {
|
||||
@@ -1056,30 +1143,28 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
/// Layer support
|
||||
|
||||
@Override protected void checkBounds(final int index) throws IOException {
|
||||
// Avoid parsing layer stuff, if we just want to read the composite data
|
||||
if (index == 0) {
|
||||
assertInput();
|
||||
readLayerAndMaskInfo(false);
|
||||
}
|
||||
else {
|
||||
super.checkBounds(index);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumImages(boolean allowSearch) throws IOException {
|
||||
// NOTE: Spec says this method should throw IllegalStateException if allowSearch && isSeekForwardOnly()
|
||||
// But that makes no sense for a format (like PSD) that does not need to search, right?
|
||||
|
||||
readHeader();
|
||||
readImageResources(false);
|
||||
readLayerAndMaskInfo(true); // TODO: Consider quicker reading of just the number of layers.
|
||||
readLayerAndMaskInfo(true);
|
||||
|
||||
return metadata.layerInfo != null ? metadata.layerInfo.size() + 1 : 1; // TODO: Only plus one, if "has real merged data"?
|
||||
}
|
||||
|
||||
// TODO: For now, leave as Metadata
|
||||
|
||||
/*
|
||||
|
||||
// ?
|
||||
Point getOffset(int pImageIndex) throws IOException;
|
||||
// Return 0, 0 for index 0, otherwise use layer offset
|
||||
|
||||
*/
|
||||
|
||||
/// Metadata support
|
||||
// TODO
|
||||
|
||||
@Override
|
||||
public IIOMetadata getStreamMetadata() throws IOException {
|
||||
@@ -1090,20 +1175,15 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||
// TODO: Implement
|
||||
checkBounds(imageIndex);
|
||||
|
||||
readHeader();
|
||||
readImageResources(true);
|
||||
readLayerAndMaskInfo(true);
|
||||
|
||||
// TODO: Need to make sure compression is set in metadata, even without reading the image data!
|
||||
// NOTE: Need to make sure compression is set in metadata, even without reading the image data!
|
||||
imageInput.seek(metadata.imageDataStart);
|
||||
metadata.compression = imageInput.readShort();
|
||||
|
||||
// metadata.header = header;
|
||||
// metadata.colorData = colorData;
|
||||
// metadata.imageResources = imageResources;
|
||||
|
||||
return metadata; // TODO: clone if we change to mutable metadata
|
||||
}
|
||||
|
||||
@@ -1129,8 +1209,6 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
if (metadata.imageResources == null) {
|
||||
// TODO: Need flag here, to specify what resources to read...
|
||||
readImageResources(true);
|
||||
// TODO: Skip this, requires storing some stream offsets
|
||||
readLayerAndMaskInfo(false);
|
||||
}
|
||||
|
||||
for (PSDImageResource resource : metadata.imageResources) {
|
||||
@@ -1236,84 +1314,89 @@ public class PSDImageReader extends ImageReaderBase {
|
||||
|
||||
PSDImageReader imageReader = new PSDImageReader(null);
|
||||
|
||||
File file = new File(pArgs[idx]);
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(file);
|
||||
imageReader.setInput(stream);
|
||||
for (; idx < pArgs.length; idx++) {
|
||||
File file = new File(pArgs[idx]);
|
||||
System.out.println();
|
||||
System.out.println("file: " + file.getAbsolutePath());
|
||||
|
||||
imageReader.readHeader();
|
||||
// System.out.println("imageReader.header: " + imageReader.header);
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(file);
|
||||
imageReader.setInput(stream);
|
||||
imageReader.readHeader();
|
||||
System.out.println("imageReader.header: " + imageReader.header);
|
||||
|
||||
imageReader.readImageResources(true);
|
||||
System.out.println("imageReader.imageResources: " + imageReader.metadata.imageResources);
|
||||
System.out.println();
|
||||
imageReader.readImageResources(true);
|
||||
System.out.println("imageReader.imageResources: " + imageReader.metadata.imageResources);
|
||||
System.out.println();
|
||||
|
||||
imageReader.readLayerAndMaskInfo(true);
|
||||
System.out.println("imageReader.layerInfo: " + imageReader.metadata.layerInfo);
|
||||
// System.out.println("imageReader.globalLayerMask: " + imageReader.globalLayerMask);
|
||||
System.out.println();
|
||||
imageReader.readLayerAndMaskInfo(true);
|
||||
System.out.println("imageReader.layerInfo: " + imageReader.metadata.layerInfo);
|
||||
/*
|
||||
// System.out.println("imageReader.globalLayerMask: " + imageReader.globalLayerMask);
|
||||
System.out.println();
|
||||
|
||||
IIOMetadata metadata = imageReader.getImageMetadata(0);
|
||||
Node node;
|
||||
XMLSerializer serializer;
|
||||
IIOMetadata metadata = imageReader.getImageMetadata(0);
|
||||
Node node;
|
||||
XMLSerializer serializer;
|
||||
|
||||
node = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||
serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
|
||||
serializer.serialize(node, true);
|
||||
System.out.println();
|
||||
node = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||
serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
|
||||
serializer.serialize(node, true);
|
||||
System.out.println();
|
||||
|
||||
node = metadata.getAsTree(PSDMetadata.NATIVE_METADATA_FORMAT_NAME);
|
||||
// serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
|
||||
serializer.serialize(node, true);
|
||||
|
||||
if (readThumbnails && imageReader.hasThumbnails(0)) {
|
||||
int thumbnails = imageReader.getNumThumbnails(0);
|
||||
for (int i = 0; i < thumbnails; i++) {
|
||||
showIt(imageReader.readThumbnail(0, i), String.format("Thumbnail %d", i));
|
||||
node = metadata.getAsTree(PSDMetadata.NATIVE_METADATA_FORMAT_NAME);
|
||||
// serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
|
||||
serializer.serialize(node, true);
|
||||
*/
|
||||
if (readThumbnails && imageReader.hasThumbnails(0)) {
|
||||
int thumbnails = imageReader.getNumThumbnails(0);
|
||||
for (int i = 0; i < thumbnails; i++) {
|
||||
showIt(imageReader.readThumbnail(0, i), String.format("Thumbnail %d", i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
ImageReadParam param = imageReader.getDefaultReadParam();
|
||||
ImageReadParam param = imageReader.getDefaultReadParam();
|
||||
|
||||
if (sourceRegion != null) {
|
||||
param.setSourceRegion(sourceRegion);
|
||||
}
|
||||
|
||||
if (subsampleFactor > 1) {
|
||||
param.setSourceSubsampling(subsampleFactor, subsampleFactor, 0, 0);
|
||||
}
|
||||
|
||||
// param.setDestinationType(imageReader.getRawImageType(0));
|
||||
|
||||
BufferedImage image = imageReader.read(0, param);
|
||||
System.out.println("read time: " + (System.currentTimeMillis() - start));
|
||||
System.out.println("image: " + image);
|
||||
|
||||
if (image.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
|
||||
try {
|
||||
ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null);
|
||||
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
|
||||
image = op.filter(image, gc.createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency()));
|
||||
if (sourceRegion != null) {
|
||||
param.setSourceRegion(sourceRegion);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
image = ImageUtil.accelerate(image);
|
||||
|
||||
if (subsampleFactor > 1) {
|
||||
param.setSourceSubsampling(subsampleFactor, subsampleFactor, 0, 0);
|
||||
}
|
||||
System.out.println("conversion time: " + (System.currentTimeMillis() - start));
|
||||
|
||||
// param.setDestinationType(imageReader.getRawImageType(0));
|
||||
|
||||
BufferedImage image = imageReader.read(0, param);
|
||||
System.out.println("read time: " + (System.currentTimeMillis() - start));
|
||||
System.out.println("image: " + image);
|
||||
}
|
||||
|
||||
showIt(image, file.getName());
|
||||
if (image.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
|
||||
try {
|
||||
ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null);
|
||||
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
|
||||
image = op.filter(image, gc.createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
image = ImageUtil.accelerate(image);
|
||||
}
|
||||
System.out.println("conversion time: " + (System.currentTimeMillis() - start));
|
||||
System.out.println("image: " + image);
|
||||
}
|
||||
|
||||
if (readLayers) {
|
||||
int images = imageReader.getNumImages(true);
|
||||
for (int i = 1; i < images; i++) {
|
||||
start = System.currentTimeMillis();
|
||||
BufferedImage layer = imageReader.read(i);
|
||||
System.out.println("layer read time: " + (System.currentTimeMillis() - start));
|
||||
System.err.println("layer: " + layer);
|
||||
showIt(layer, "layer " + i);
|
||||
showIt(image, file.getName());
|
||||
|
||||
if (readLayers) {
|
||||
int images = imageReader.getNumImages(true);
|
||||
for (int i = 1; i < images; i++) {
|
||||
start = System.currentTimeMillis();
|
||||
BufferedImage layer = imageReader.read(i);
|
||||
System.out.println("layer read time: " + (System.currentTimeMillis() - start));
|
||||
System.err.println("layer: " + layer);
|
||||
showIt(layer, "layer " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -44,7 +44,7 @@ import java.util.Locale;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDImageReaderSpi.java,v 1.0 Apr 29, 2008 4:49:03 PM haraldk Exp$
|
||||
*/
|
||||
public class PSDImageReaderSpi extends ImageReaderSpi {
|
||||
final public class PSDImageReaderSpi extends ImageReaderSpi {
|
||||
|
||||
/**
|
||||
* Creates a {@code PSDImageReaderSpi}.
|
||||
@@ -57,15 +57,18 @@ public class PSDImageReaderSpi extends ImageReaderSpi {
|
||||
super(
|
||||
providerInfo.getVendorName(),
|
||||
providerInfo.getVersion(),
|
||||
new String[]{"psd", "PSD"},
|
||||
new String[]{"psd"},
|
||||
new String[]{
|
||||
"application/vnd.adobe.photoshop", // This one seems official, used in XMP
|
||||
"image/x-psd", "application/x-photoshop", "image/x-photoshop"
|
||||
new String[] {"psd", "PSD"},
|
||||
new String[] {"psd"},
|
||||
new String[] {
|
||||
"image/vnd.adobe.photoshop", // Official, IANA registered
|
||||
"application/vnd.adobe.photoshop", // Used in XMP
|
||||
"image/x-psd",
|
||||
"application/x-photoshop",
|
||||
"image/x-photoshop"
|
||||
},
|
||||
"com.twelvemkonkeys.imageio.plugins.psd.PSDImageReader",
|
||||
new Class[] {ImageInputStream.class},
|
||||
// new String[]{"com.twelvemkonkeys.imageio.plugins.psd.PSDImageWriterSpi"},
|
||||
// new String[] {"com.twelvemkonkeys.imageio.plugins.psd.PSDImageWriterSpi"},
|
||||
null,
|
||||
true, // supports standard stream metadata
|
||||
null, null, // native stream format name and class
|
||||
@@ -84,9 +87,23 @@ public class PSDImageReaderSpi extends ImageReaderSpi {
|
||||
ImageInputStream stream = (ImageInputStream) pSource;
|
||||
|
||||
stream.mark();
|
||||
|
||||
try {
|
||||
return stream.readInt() == PSD.SIGNATURE_8BPS;
|
||||
// TODO: Test more of the header, see PSDImageReader#readHeader
|
||||
if (stream.readInt() == PSD.SIGNATURE_8BPS) {
|
||||
int version = stream.readUnsignedShort();
|
||||
|
||||
switch (version) {
|
||||
case PSD.VERSION_PSD:
|
||||
case PSD.VERSION_PSB:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
stream.reset();
|
||||
|
||||
Regular → Executable
+14
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -110,7 +110,7 @@ public class PSDImageResource {
|
||||
|
||||
builder.append("[ID: 0x");
|
||||
builder.append(Integer.toHexString(id));
|
||||
if (name != null && name.trim().length() != 0) {
|
||||
if (name != null && !name.trim().isEmpty()) {
|
||||
builder.append(", name: \"");
|
||||
builder.append(name);
|
||||
builder.append("\"");
|
||||
@@ -139,6 +139,13 @@ public class PSDImageResource {
|
||||
case PSD.RES_PRINT_FLAGS_INFORMATION:
|
||||
return null;
|
||||
default:
|
||||
if (pId >= PSD.RES_PATH_INFO_MIN && pId <= PSD.RES_PATH_INFO_MAX) {
|
||||
return "PathInformationResource";
|
||||
}
|
||||
if (pId >= PSD.RES_PLUGIN_MIN && pId <= PSD.RES_PLUGIN_MAX) {
|
||||
return "PluginResource";
|
||||
}
|
||||
|
||||
try {
|
||||
for (Field field : PSD.class.getDeclaredFields()) {
|
||||
if (field.getName().startsWith("RES_") && field.getInt(null) == pId) {
|
||||
@@ -149,7 +156,7 @@ public class PSDImageResource {
|
||||
}
|
||||
catch (IllegalAccessException ignore) {
|
||||
}
|
||||
|
||||
|
||||
return "UnknownResource";
|
||||
}
|
||||
}
|
||||
@@ -160,9 +167,9 @@ public class PSDImageResource {
|
||||
throw new IIOException(String.format("Wrong image resource type, expected '8BIM': '%s'", PSDUtil.intToStr(type)));
|
||||
}
|
||||
|
||||
// TODO: Process more of the resource stuff, most important are IPTC, EXIF and XMP data,
|
||||
// version info, and thumbnail for thumbnail-support.
|
||||
// TODO: Have PSDImageResources defer actual parsing? (Just store stream offsets)
|
||||
short id = pInput.readShort();
|
||||
|
||||
switch (id) {
|
||||
case PSD.RES_RESOLUTION_INFO:
|
||||
return new PSDResolutionInfo(id, pInput);
|
||||
@@ -196,9 +203,8 @@ public class PSDImageResource {
|
||||
case PSD.RES_PRINT_FLAGS_INFORMATION:
|
||||
return new PSDPrintFlagsInformation(id, pInput);
|
||||
default:
|
||||
if (id >= 0x07d0 && id <= 0x0bb6) {
|
||||
// TODO: Parse saved path information
|
||||
return new PSDImageResource(id, pInput);
|
||||
if (id >= PSD.RES_PATH_INFO_MIN && id <= PSD.RES_PATH_INFO_MAX) {
|
||||
return new PSDPathResource(id, pInput);
|
||||
}
|
||||
else {
|
||||
return new PSDImageResource(id, pInput);
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -39,7 +39,7 @@ import java.io.IOException;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDLayerBlendMode.java,v 1.0 May 8, 2008 4:34:35 PM haraldk Exp$
|
||||
*/
|
||||
class PSDLayerBlendMode {
|
||||
final class PSDLayerBlendMode {
|
||||
final int blendMode;
|
||||
final int opacity; // 0-255
|
||||
final int clipping; // 0: base, 1: non-base
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -40,7 +40,7 @@ import java.util.Arrays;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDLayerInfo.java,v 1.0 Apr 29, 2008 6:01:12 PM haraldk Exp$
|
||||
*/
|
||||
class PSDLayerInfo {
|
||||
final class PSDLayerInfo {
|
||||
final int top;
|
||||
final int left;
|
||||
final int bottom;
|
||||
@@ -52,7 +52,7 @@ class PSDLayerInfo {
|
||||
final PSDChannelSourceDestinationRange[] ranges;
|
||||
final String layerName;
|
||||
|
||||
PSDLayerInfo(ImageInputStream pInput) throws IOException {
|
||||
PSDLayerInfo(final boolean largeFormat, final ImageInputStream pInput) throws IOException {
|
||||
top = pInput.readInt();
|
||||
left = pInput.readInt();
|
||||
bottom = pInput.readInt();
|
||||
@@ -63,7 +63,7 @@ class PSDLayerInfo {
|
||||
channelInfo = new PSDChannelInfo[channels];
|
||||
for (int i = 0; i < channels; i++) {
|
||||
short channelId = pInput.readShort();
|
||||
long length = pInput.readUnsignedInt();
|
||||
long length = largeFormat ? pInput.readLong() : pInput.readUnsignedInt();
|
||||
|
||||
channelInfo[i] = new PSDChannelInfo(channelId, length);
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -39,7 +39,7 @@ import java.io.IOException;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDLayerMaskData.java,v 1.0 May 6, 2008 5:15:05 PM haraldk Exp$
|
||||
*/
|
||||
class PSDLayerMaskData {
|
||||
final class PSDLayerMaskData {
|
||||
private int top;
|
||||
private int left;
|
||||
private int bottom;
|
||||
|
||||
Regular → Executable
+104
-116
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
@@ -35,7 +63,11 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
List<PSDImageResource> imageResources;
|
||||
PSDGlobalLayerMask globalLayerMask;
|
||||
List<PSDLayerInfo> layerInfo;
|
||||
|
||||
long imageResourcesStart;
|
||||
long layerAndMaskInfoStart;
|
||||
long layersStart;
|
||||
long imageDataStart;
|
||||
|
||||
static final String[] COLOR_MODES = {
|
||||
"MONOCHROME", "GRAYSCALE", "INDEXED", "RGB", "CMYK", null, null, "MULTICHANNEL", "DUOTONE", "LAB"
|
||||
@@ -85,17 +117,17 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
}
|
||||
|
||||
private Node createHeaderNode() {
|
||||
IIOMetadataNode header = new IIOMetadataNode("Header");
|
||||
IIOMetadataNode headerNode = new IIOMetadataNode("Header");
|
||||
|
||||
header.setAttribute("type", "PSD");
|
||||
header.setAttribute("version", "1");
|
||||
header.setAttribute("channels", Integer.toString(this.header.channels));
|
||||
header.setAttribute("height", Integer.toString(this.header.height));
|
||||
header.setAttribute("width", Integer.toString(this.header.width));
|
||||
header.setAttribute("bits", Integer.toString(this.header.bits));
|
||||
header.setAttribute("mode", COLOR_MODES[this.header.mode]);
|
||||
headerNode.setAttribute("type", "PSD");
|
||||
headerNode.setAttribute("version", header.largeFormat ? "2" : "1");
|
||||
headerNode.setAttribute("channels", Integer.toString(header.channels));
|
||||
headerNode.setAttribute("height", Integer.toString(header.height));
|
||||
headerNode.setAttribute("width", Integer.toString(header.width));
|
||||
headerNode.setAttribute("bits", Integer.toString(header.bits));
|
||||
headerNode.setAttribute("mode", COLOR_MODES[header.mode]);
|
||||
|
||||
return header;
|
||||
return headerNode;
|
||||
}
|
||||
|
||||
private Node createImageResourcesNode() {
|
||||
@@ -112,18 +144,6 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
// TODO: Format spec
|
||||
node = new IIOMetadataNode("ICCProfile");
|
||||
node.setAttribute("colorSpaceType", JAVA_CS[profile.getProfile().getColorSpaceType()]);
|
||||
//
|
||||
// FastByteArrayOutputStream data = new FastByteArrayOutputStream(0);
|
||||
// EncoderStream base64 = new EncoderStream(data, new Base64Encoder(), true);
|
||||
//
|
||||
// try {
|
||||
// base64.write(profile.getProfile().getData());
|
||||
// }
|
||||
// catch (IOException ignore) {
|
||||
// }
|
||||
//
|
||||
// byte[] bytes = data.toByteArray();
|
||||
// node.setAttribute("data", StringUtil.decode(bytes, 0, bytes.length, "ASCII"));
|
||||
node.setUserObject(profile.getProfile());
|
||||
}
|
||||
else if (imageResource instanceof PSDAlphaChannelInfo) {
|
||||
@@ -347,13 +367,12 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma");
|
||||
IIOMetadataNode node; // scratch node
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
node = new IIOMetadataNode("ColorSpaceType");
|
||||
IIOMetadataNode colorSpaceType = new IIOMetadataNode("ColorSpaceType");
|
||||
String cs;
|
||||
switch (header.mode) {
|
||||
case PSD.COLOR_MODE_MONOCHROME:
|
||||
case PSD.COLOR_MODE_BITMAP:
|
||||
case PSD.COLOR_MODE_GRAYSCALE:
|
||||
case PSD.COLOR_MODE_DUOTONE: // Rationale: Spec says treat as gray...
|
||||
cs = "GRAY";
|
||||
@@ -374,25 +393,24 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
default:
|
||||
throw new AssertionError("Unreachable");
|
||||
}
|
||||
node.setAttribute("name", cs);
|
||||
chroma_node.appendChild(node);
|
||||
colorSpaceType.setAttribute("name", cs);
|
||||
chroma.appendChild(colorSpaceType);
|
||||
|
||||
// TODO: Channels might be 5 for RGB + A + Mask... Probably not correct
|
||||
node = new IIOMetadataNode("NumChannels");
|
||||
node.setAttribute("value", Integer.toString(header.channels));
|
||||
chroma_node.appendChild(node);
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
numChannels.setAttribute("value", Integer.toString(header.channels));
|
||||
chroma.appendChild(numChannels);
|
||||
|
||||
// TODO: Check if this is correct with bitmap (monchrome)
|
||||
node = new IIOMetadataNode("BlackIsZero");
|
||||
node.setAttribute("value", "true");
|
||||
chroma_node.appendChild(node);
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
blackIsZero.setAttribute("value", "true");
|
||||
chroma.appendChild(blackIsZero);
|
||||
|
||||
if (header.mode == PSD.COLOR_MODE_INDEXED) {
|
||||
node = createPaletteNode();
|
||||
chroma_node.appendChild(node);
|
||||
IIOMetadataNode paletteNode = createPaletteNode();
|
||||
chroma.appendChild(paletteNode);
|
||||
}
|
||||
|
||||
// TODO: Hardcode background color to white?
|
||||
// TODO: Use
|
||||
// if (bKGD_present) {
|
||||
// if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
|
||||
// node = new IIOMetadataNode("BackgroundIndex");
|
||||
@@ -415,24 +433,25 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
// chroma_node.appendChild(node);
|
||||
// }
|
||||
|
||||
return chroma_node;
|
||||
return chroma;
|
||||
}
|
||||
|
||||
private IIOMetadataNode createPaletteNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Palette");
|
||||
IIOMetadataNode palette = new IIOMetadataNode("Palette");
|
||||
IndexColorModel cm = colorData.getIndexColorModel();
|
||||
|
||||
for (int i = 0; i < cm.getMapSize(); i++) {
|
||||
IIOMetadataNode entry = new IIOMetadataNode("PaletteEntry");
|
||||
|
||||
entry.setAttribute("index", Integer.toString(i));
|
||||
entry.setAttribute("red", Integer.toString(cm.getRed(i)));
|
||||
entry.setAttribute("green", Integer.toString(cm.getGreen(i)));
|
||||
entry.setAttribute("blue", Integer.toString(cm.getBlue(i)));
|
||||
|
||||
node.appendChild(entry);
|
||||
palette.appendChild(entry);
|
||||
}
|
||||
|
||||
return node;
|
||||
return palette;
|
||||
}
|
||||
|
||||
private String getMultiChannelCS(short channels) {
|
||||
@@ -446,33 +465,33 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
IIOMetadataNode compressionNode = new IIOMetadataNode("Compression");
|
||||
IIOMetadataNode node; // scratch node
|
||||
|
||||
node = new IIOMetadataNode("CompressionTypeName");
|
||||
String compression;
|
||||
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||
String compressionName;
|
||||
|
||||
switch (this.compression) {
|
||||
switch (compression) {
|
||||
case PSD.COMPRESSION_NONE:
|
||||
compression = "none";
|
||||
compressionName = "none";
|
||||
break;
|
||||
case PSD.COMPRESSION_RLE:
|
||||
compression = "PackBits";
|
||||
compressionName = "PackBits";
|
||||
break;
|
||||
case PSD.COMPRESSION_ZIP:
|
||||
case PSD.COMPRESSION_ZIP_PREDICTION:
|
||||
compression = "Deflate"; // TODO: ZLib? (TIFF native metadata format specifies both.. :-P)
|
||||
compressionName = "Zip";
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Unreachable");
|
||||
}
|
||||
|
||||
node.setAttribute("value", compression);
|
||||
compressionNode.appendChild(node);
|
||||
compressionTypeName.setAttribute("value", compressionName);
|
||||
compressionNode.appendChild(compressionTypeName);
|
||||
|
||||
// TODO: Does it make sense to specify lossless for compression "none"?
|
||||
node = new IIOMetadataNode("Lossless");
|
||||
node.setAttribute("value", "true");
|
||||
compressionNode.appendChild(node);
|
||||
if (compression != PSD.COMPRESSION_NONE) {
|
||||
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
||||
lossless.setAttribute("value", "true");
|
||||
compressionNode.appendChild(lossless);
|
||||
}
|
||||
|
||||
return compressionNode;
|
||||
}
|
||||
@@ -480,15 +499,14 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode dataNode = new IIOMetadataNode("Data");
|
||||
IIOMetadataNode node; // scratch node
|
||||
|
||||
node = new IIOMetadataNode("PlanarConfiguration");
|
||||
node.setAttribute("value", "PlaneInterleaved"); // TODO: Check with spec
|
||||
dataNode.appendChild(node);
|
||||
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||
planarConfiguration.setAttribute("value", "PlaneInterleaved");
|
||||
dataNode.appendChild(planarConfiguration);
|
||||
|
||||
node = new IIOMetadataNode("SampleFormat");
|
||||
node.setAttribute("value", header.mode == PSD.COLOR_MODE_INDEXED ? "Index" : "UnsignedIntegral");
|
||||
dataNode.appendChild(node);
|
||||
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||
sampleFormat.setAttribute("value", header.mode == PSD.COLOR_MODE_INDEXED ? "Index" : "UnsignedIntegral");
|
||||
dataNode.appendChild(sampleFormat);
|
||||
|
||||
String bitDepth = Integer.toString(header.bits); // bits per plane
|
||||
|
||||
@@ -496,11 +514,9 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
String[] bps = new String[header.channels];
|
||||
Arrays.fill(bps, bitDepth);
|
||||
|
||||
node = new IIOMetadataNode("BitsPerSample");
|
||||
node.setAttribute("value", StringUtil.toCSVString(bps, " "));
|
||||
dataNode.appendChild(node);
|
||||
|
||||
// TODO: SampleMSB? Or is network (aka Motorola/big endian) byte order assumed?
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
bitsPerSample.setAttribute("value", StringUtil.toCSVString(bps, " "));
|
||||
dataNode.appendChild(bitsPerSample);
|
||||
|
||||
return dataNode;
|
||||
}
|
||||
@@ -542,33 +558,6 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
dimensionNode.appendChild(node);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
/*
|
||||
<!ELEMENT "HorizontalPixelOffset" EMPTY>
|
||||
<!-- The horizonal position, in pixels, where the image should be
|
||||
rendered onto a raster display -->
|
||||
<!ATTLIST "HorizontalPixelOffset" "value" #CDATA #REQUIRED>
|
||||
<!-- Data type: Integer -->
|
||||
|
||||
<!ELEMENT "VerticalPixelOffset" EMPTY>
|
||||
<!-- The vertical position, in pixels, where the image should be
|
||||
rendered onto a raster display -->
|
||||
<!ATTLIST "VerticalPixelOffset" "value" #CDATA #REQUIRED>
|
||||
<!-- Data type: Integer -->
|
||||
|
||||
<!ELEMENT "HorizontalScreenSize" EMPTY>
|
||||
<!-- The width, in pixels, of the raster display into which the
|
||||
image should be rendered -->
|
||||
<!ATTLIST "HorizontalScreenSize" "value" #CDATA #REQUIRED>
|
||||
<!-- Data type: Integer -->
|
||||
|
||||
<!ELEMENT "VerticalScreenSize" EMPTY>
|
||||
<!-- The height, in pixels, of the raster display into which the
|
||||
image should be rendered -->
|
||||
<!ATTLIST "VerticalScreenSize" "value" #CDATA #REQUIRED>
|
||||
<!-- Data type: Integer -->
|
||||
|
||||
*/
|
||||
return dimensionNode;
|
||||
}
|
||||
|
||||
@@ -580,11 +569,10 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDocumentNode() {
|
||||
IIOMetadataNode document_node = new IIOMetadataNode("Document");
|
||||
IIOMetadataNode node; // scratch node
|
||||
|
||||
node = new IIOMetadataNode("FormatVersion");
|
||||
node.setAttribute("value", "1"); // PSD format version is always 1
|
||||
document_node.appendChild(node);
|
||||
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
|
||||
formatVersion.setAttribute("value", header.largeFormat ? "2" : "1"); // PSD format version is always 1, PSB is 2
|
||||
document_node.appendChild(formatVersion);
|
||||
|
||||
// Get EXIF data if present
|
||||
Iterator<PSDEXIF1Data> exif = getResources(PSDEXIF1Data.class);
|
||||
@@ -594,18 +582,18 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
// Get the EXIF DateTime (aka ModifyDate) tag if present
|
||||
Entry dateTime = data.directory.getEntryById(TIFF.TAG_DATE_TIME);
|
||||
if (dateTime != null) {
|
||||
node = new IIOMetadataNode("ImageCreationTime"); // As TIFF, but could just as well be ImageModificationTime
|
||||
IIOMetadataNode imageCreationTime = new IIOMetadataNode("ImageCreationTime"); // As TIFF, but could just as well be ImageModificationTime
|
||||
// Format: "YYYY:MM:DD hh:mm:ss"
|
||||
String value = dateTime.getValueAsString();
|
||||
|
||||
node.setAttribute("year", value.substring(0, 4));
|
||||
node.setAttribute("month", value.substring(5, 7));
|
||||
node.setAttribute("day", value.substring(8, 10));
|
||||
node.setAttribute("hour", value.substring(11, 13));
|
||||
node.setAttribute("minute", value.substring(14, 16));
|
||||
node.setAttribute("second", value.substring(17, 19));
|
||||
imageCreationTime.setAttribute("year", value.substring(0, 4));
|
||||
imageCreationTime.setAttribute("month", value.substring(5, 7));
|
||||
imageCreationTime.setAttribute("day", value.substring(8, 10));
|
||||
imageCreationTime.setAttribute("hour", value.substring(11, 13));
|
||||
imageCreationTime.setAttribute("minute", value.substring(14, 16));
|
||||
imageCreationTime.setAttribute("second", value.substring(17, 19));
|
||||
|
||||
document_node.appendChild(node);
|
||||
document_node.appendChild(imageCreationTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,7 +602,7 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTextNode() {
|
||||
// TODO: TIFF uses
|
||||
// NOTE: TIFF uses
|
||||
// DocumentName, ImageDescription, Make, Model, PageName, Software, Artist, HostComputer, InkNames, Copyright:
|
||||
// /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value.
|
||||
// Example: TIFF Software field => /Text/TextEntry@keyword = "Software",
|
||||
@@ -679,9 +667,10 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
}
|
||||
|
||||
private void appendTextEntriesFlat(final IIOMetadataNode node, final Directory directory, final FilterIterator.Filter<Entry> filter) {
|
||||
FilterIterator<Entry> pEntries = new FilterIterator<Entry>(directory.iterator(), filter);
|
||||
while (pEntries.hasNext()) {
|
||||
Entry entry = pEntries.next();
|
||||
FilterIterator<Entry> entries = new FilterIterator<Entry>(directory.iterator(), filter);
|
||||
|
||||
while (entries.hasNext()) {
|
||||
Entry entry = entries.next();
|
||||
|
||||
if (entry.getValue() instanceof Directory) {
|
||||
appendTextEntriesFlat(node, (Directory) entry.getValue(), filter);
|
||||
@@ -694,7 +683,7 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
tag.setAttribute("keyword", fieldName);
|
||||
}
|
||||
else {
|
||||
// TODO: This should never happen, as we filter out only specific nodes
|
||||
// NOTE: This should never happen, as we filter out only specific nodes
|
||||
tag.setAttribute("keyword", String.format("%s", entry.getIdentifier()));
|
||||
}
|
||||
|
||||
@@ -711,14 +700,13 @@ public final class PSDMetadata extends AbstractMetadata {
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
IIOMetadataNode transparency_node = new IIOMetadataNode("Transparency");
|
||||
IIOMetadataNode node; // scratch node
|
||||
IIOMetadataNode transparencyNode = new IIOMetadataNode("Transparency");
|
||||
|
||||
node = new IIOMetadataNode("Alpha");
|
||||
node.setAttribute("value", hasAlpha() ? "nonpremultiplied" : "none"); // TODO: Check spec
|
||||
transparency_node.appendChild(node);
|
||||
IIOMetadataNode node = new IIOMetadataNode("Alpha");
|
||||
node.setAttribute("value", hasAlpha() ? "premultiplied" : "none");
|
||||
transparencyNode.appendChild(node);
|
||||
|
||||
return transparency_node;
|
||||
return transparencyNode;
|
||||
}
|
||||
|
||||
private boolean hasAlpha() {
|
||||
|
||||
Regular → Executable
+30
-2
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
@@ -17,7 +45,7 @@ import java.util.Arrays;
|
||||
*/
|
||||
public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
|
||||
|
||||
private final static PSDMetadataFormat sInstance = new PSDMetadataFormat();
|
||||
private static final PSDMetadataFormat instance = new PSDMetadataFormat();
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
@@ -207,6 +235,6 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
|
||||
* @see javax.imageio.metadata.IIOMetadata#getMetadataFormat
|
||||
*/
|
||||
public static PSDMetadataFormat getInstance() {
|
||||
return sInstance;
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* PSDPathResource
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDPathResource.java,v 1.0 Sept 4, 2014 8:23:09 PM haraldk Exp$
|
||||
*/
|
||||
final class PSDPathResource extends PSDImageResource {
|
||||
PSDPathResource(final short resourceId, final ImageInputStream input) throws IOException {
|
||||
super(resourceId, input);
|
||||
}
|
||||
}
|
||||
+28
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
Regular → Executable
+34
-2
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
@@ -33,9 +61,13 @@ final class PSDPrintFlags extends PSDImageResource {
|
||||
negative = pInput.readBoolean();
|
||||
flip = pInput.readBoolean();
|
||||
interpolate = pInput.readBoolean();
|
||||
caption = pInput.readBoolean();
|
||||
|
||||
pInput.skipBytes(size - 8);
|
||||
// Photoshop 2.5 and before has shorter struct
|
||||
if (size > 7) {
|
||||
caption = pInput.readBoolean();
|
||||
|
||||
pInput.skipBytes(size - 8);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+28
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
+28
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -39,7 +39,7 @@ import java.io.IOException;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$
|
||||
*/
|
||||
class PSDResolutionInfo extends PSDImageResource {
|
||||
final class PSDResolutionInfo extends PSDImageResource {
|
||||
// typedef struct _ResolutionInfo
|
||||
// {
|
||||
// LONG hRes; /* Fixed-point number: pixels per inch */
|
||||
|
||||
Regular → Executable
+29
-1
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
@@ -16,7 +44,7 @@ import java.io.IOException;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDThumbnail.java,v 1.0 Jul 29, 2009 4:41:06 PM haraldk Exp$
|
||||
*/
|
||||
class PSDThumbnail extends PSDImageResource {
|
||||
final class PSDThumbnail extends PSDImageResource {
|
||||
private int format;
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
+28
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
||||
+28
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
+28
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
|
||||
Regular → Executable
+55
-3
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.psd;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
@@ -27,7 +55,7 @@ import static org.junit.Assert.*;
|
||||
*/
|
||||
public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImageReader> {
|
||||
|
||||
static ImageReaderSpi provider = new PSDImageReaderSpi();
|
||||
private static final ImageReaderSpi provider = new PSDImageReaderSpi();
|
||||
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
@@ -48,7 +76,21 @@ public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImage
|
||||
// 1 channel, gray, 16 bit samples
|
||||
new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)),
|
||||
// 4 channel, CMYK, 16 bit samples
|
||||
new TestData(getClassLoaderResource("/psd/cmyk_16bits.psd"), new Dimension(1000, 275))
|
||||
new TestData(getClassLoaderResource("/psd/cmyk_16bits.psd"), new Dimension(1000, 275)),
|
||||
// 3 channel, RGB, 32 bit samples
|
||||
new TestData(getClassLoaderResource("/psd/32bit5x5.psd"), new Dimension(5, 5)),
|
||||
// 3 channel, RGB, 8 bit samples ("Large Document Format" aka PSB)
|
||||
new TestData(getClassLoaderResource("/psd/test_original.psb"), new Dimension(710, 512)),
|
||||
// From http://telegraphics.com.au/svn/psdparse/trunk/psd/
|
||||
new TestData(getClassLoaderResource("/psd/adobehq.psd"), new Dimension(341, 512)),
|
||||
new TestData(getClassLoaderResource("/psd/adobehq_ind.psd"), new Dimension(341, 512)),
|
||||
// Contains a shorter than normal PrintFlags chunk
|
||||
new TestData(getClassLoaderResource("/psd/adobehq-2.5.psd"), new Dimension(341, 512)),
|
||||
new TestData(getClassLoaderResource("/psd/adobehq-3.0.psd"), new Dimension(341, 512)),
|
||||
new TestData(getClassLoaderResource("/psd/adobehq-5.5.psd"), new Dimension(341, 512)),
|
||||
new TestData(getClassLoaderResource("/psd/adobehq-7.0.psd"), new Dimension(341, 512)),
|
||||
// From https://github.com/kmike/psd-tools/tree/master/tests/psd_files
|
||||
new TestData(getClassLoaderResource("/psd/masks2.psd"), new Dimension(640, 1136)) // TODO: Test read layers!
|
||||
// TODO: Need uncompressed PSD
|
||||
// TODO: Need more recent ZIP compressed PSD files from CS2/CS3+
|
||||
);
|
||||
@@ -76,14 +118,20 @@ public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImage
|
||||
}
|
||||
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/x-psd");
|
||||
return Arrays.asList(
|
||||
"image/vnd.adobe.photoshop",
|
||||
"application/vnd.adobe.photoshop",
|
||||
"image/x-psd"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSupportsThumbnail() {
|
||||
PSDImageReader imageReader = createReader();
|
||||
assertTrue(imageReader.readerSupportsThumbnails());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThumbnailReading() throws IOException {
|
||||
PSDImageReader imageReader = createReader();
|
||||
|
||||
@@ -98,6 +146,7 @@ public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImage
|
||||
assertEquals(96, thumbnail.getHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThumbnailReadingNoInput() throws IOException {
|
||||
PSDImageReader imageReader = createReader();
|
||||
|
||||
@@ -134,6 +183,7 @@ public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImage
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThumbnailReadingOutOfBounds() throws IOException {
|
||||
PSDImageReader imageReader = createReader();
|
||||
|
||||
@@ -175,6 +225,7 @@ public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImage
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThumbnailDimensions() throws IOException {
|
||||
PSDImageReader imageReader = createReader();
|
||||
|
||||
@@ -186,6 +237,7 @@ public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImage
|
||||
assertEquals(96, imageReader.getThumbnailHeight(0, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThumbnailReadListeners() throws IOException {
|
||||
PSDImageReader imageReader = createReader();
|
||||
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-reference</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: reference test cases</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-thumbsdb</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Thumbs.db plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.0-rc5</version>
|
||||
<version>3.0.1</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TIFF plugin</name>
|
||||
|
||||
+2
-1
@@ -81,6 +81,7 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
|
||||
private void fetch() throws IOException {
|
||||
if (decodedPos >= decodedLength) {
|
||||
decodedLength = 0;
|
||||
|
||||
try {
|
||||
decodeRow();
|
||||
}
|
||||
@@ -179,7 +180,7 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
|
||||
throw new IOException("Sum of run-lengths does not equal scan line width: " + index + " > " + columns);
|
||||
}
|
||||
|
||||
decodedLength = (index / 8) + 1;
|
||||
decodedLength = (index + 7) / 8;
|
||||
}
|
||||
|
||||
private int decodeRun(short[][] codes, short[][] runLengths, int minCodeSize) throws IOException {
|
||||
|
||||
+14
-9
@@ -57,12 +57,12 @@ final class HorizontalDeDifferencingStream extends InputStream {
|
||||
private final ByteBuffer buffer;
|
||||
|
||||
public HorizontalDeDifferencingStream(final InputStream stream, final int columns, final int samplesPerPixel, final int bitsPerSample, final ByteOrder byteOrder) {
|
||||
channel = Channels.newChannel(Validate.notNull(stream, "stream"));
|
||||
|
||||
this.columns = Validate.isTrue(columns > 0, columns, "width must be greater than 0");
|
||||
this.samplesPerPixel = Validate.isTrue(bitsPerSample >= 8 || samplesPerPixel == 1, samplesPerPixel, "Unsupported samples per pixel for < 8 bit samples: %s");
|
||||
this.bitsPerSample = Validate.isTrue(isValidBPS(bitsPerSample), bitsPerSample, "Unsupported bits per sample value: %s");
|
||||
|
||||
channel = Channels.newChannel(Validate.notNull(stream, "stream"));
|
||||
|
||||
buffer = ByteBuffer.allocate((columns * samplesPerPixel * bitsPerSample + 7) / 8).order(byteOrder);
|
||||
buffer.flip();
|
||||
}
|
||||
@@ -113,10 +113,15 @@ final class HorizontalDeDifferencingStream extends InputStream {
|
||||
int sample = 0;
|
||||
byte temp;
|
||||
|
||||
// Optimization:
|
||||
// Access array directly for <= 8 bits per sample, as buffer does extra index bounds check for every
|
||||
// put/get operation... (Measures to about 100 ms difference for 4000 x 3000 image)
|
||||
final byte[] array = buffer.array();
|
||||
|
||||
switch (bitsPerSample) {
|
||||
case 1:
|
||||
for (int b = 0; b < (columns + 7) / 8; b++) {
|
||||
original = buffer.get(b);
|
||||
original = array[b];
|
||||
sample += (original >> 7) & 0x1;
|
||||
temp = (byte) ((sample << 7) & 0x80);
|
||||
sample += (original >> 6) & 0x1;
|
||||
@@ -132,13 +137,13 @@ final class HorizontalDeDifferencingStream extends InputStream {
|
||||
sample += (original >> 1) & 0x1;
|
||||
temp |= (byte) ((sample << 1) & 0x02);
|
||||
sample += original & 0x1;
|
||||
buffer.put(b, (byte) (temp | sample & 0x1));
|
||||
array[b] = (byte) (temp | sample & 0x1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
for (int b = 0; b < (columns + 3) / 4; b++) {
|
||||
original = buffer.get(b);
|
||||
original = array[b];
|
||||
sample += (original >> 6) & 0x3;
|
||||
temp = (byte) ((sample << 6) & 0xc0);
|
||||
sample += (original >> 4) & 0x3;
|
||||
@@ -146,17 +151,17 @@ final class HorizontalDeDifferencingStream extends InputStream {
|
||||
sample += (original >> 2) & 0x3;
|
||||
temp |= (byte) ((sample << 2) & 0x0c);
|
||||
sample += original & 0x3;
|
||||
buffer.put(b, (byte) (temp | sample & 0x3));
|
||||
array[b] = (byte) (temp | sample & 0x3);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
for (int b = 0; b < (columns + 1) / 2; b++) {
|
||||
original = buffer.get(b);
|
||||
original = array[b];
|
||||
sample += (original >> 4) & 0xf;
|
||||
temp = (byte) ((sample << 4) & 0xf0);
|
||||
sample += original & 0x0f;
|
||||
buffer.put(b, (byte) (temp | sample & 0xf));
|
||||
array[b] = (byte) (temp | sample & 0xf);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -164,7 +169,7 @@ final class HorizontalDeDifferencingStream extends InputStream {
|
||||
for (int x = 1; x < columns; x++) {
|
||||
for (int b = 0; b < samplesPerPixel; b++) {
|
||||
int off = x * samplesPerPixel + b;
|
||||
buffer.put(off, (byte) (buffer.get(off - samplesPerPixel) + buffer.get(off)));
|
||||
array[off] = (byte) (array[off - samplesPerPixel] + array[off]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
+59
-13
@@ -33,6 +33,7 @@ import com.twelvemonkeys.io.enc.Decoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.String;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
@@ -58,7 +59,7 @@ abstract class LZWDecoder implements Decoder {
|
||||
|
||||
private final boolean compatibilityMode;
|
||||
|
||||
private final String[] table;
|
||||
private final LZWString[] table;
|
||||
private int tableLength;
|
||||
int bitsPerCode;
|
||||
private int oldCode = CLEAR_CODE;
|
||||
@@ -73,11 +74,11 @@ abstract class LZWDecoder implements Decoder {
|
||||
protected LZWDecoder(final boolean compatibilityMode) {
|
||||
this.compatibilityMode = compatibilityMode;
|
||||
|
||||
table = new String[compatibilityMode ? TABLE_SIZE + 1024 : TABLE_SIZE]; // libTiff adds 1024 "for compatibility"...
|
||||
table = new LZWString[compatibilityMode ? TABLE_SIZE + 1024 : TABLE_SIZE]; // libTiff adds 1024 "for compatibility"...
|
||||
|
||||
// First 258 entries of table is always fixed
|
||||
for (int i = 0; i < 256; i++) {
|
||||
table[i] = new String((byte) i);
|
||||
table[i] = new LZWString((byte) i);
|
||||
}
|
||||
|
||||
init();
|
||||
@@ -112,12 +113,17 @@ abstract class LZWDecoder implements Decoder {
|
||||
table[code].writeTo(buffer);
|
||||
}
|
||||
else {
|
||||
if (table[oldCode] == null) {
|
||||
System.err.println("tableLength: " + tableLength);
|
||||
System.err.println("oldCode: " + oldCode);
|
||||
}
|
||||
|
||||
if (isInTable(code)) {
|
||||
table[code].writeTo(buffer);
|
||||
addStringToTable(table[oldCode].concatenate(table[code].firstChar));
|
||||
}
|
||||
else {
|
||||
String outString = table[oldCode].concatenate(table[oldCode].firstChar);
|
||||
LZWString outString = table[oldCode].concatenate(table[oldCode].firstChar);
|
||||
|
||||
outString.writeTo(buffer);
|
||||
addStringToTable(outString);
|
||||
@@ -135,7 +141,7 @@ abstract class LZWDecoder implements Decoder {
|
||||
return buffer.position();
|
||||
}
|
||||
|
||||
private void addStringToTable(final String string) throws IOException {
|
||||
private void addStringToTable(final LZWString string) throws IOException {
|
||||
table[tableLength++] = string;
|
||||
|
||||
if (tableLength > maxCode) {
|
||||
@@ -146,7 +152,7 @@ abstract class LZWDecoder implements Decoder {
|
||||
bitsPerCode--;
|
||||
}
|
||||
else {
|
||||
throw new DecodeException(java.lang.String.format("TIFF LZW with more than %d bits per code encountered (table overflow)", MAX_BITS));
|
||||
throw new DecodeException(String.format("TIFF LZW with more than %d bits per code encountered (table overflow)", MAX_BITS));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,26 +285,26 @@ abstract class LZWDecoder implements Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class String {
|
||||
final String previous;
|
||||
static final class LZWString {
|
||||
final LZWString previous;
|
||||
|
||||
final int length;
|
||||
final byte value;
|
||||
final byte firstChar; // Copied forward for fast access
|
||||
|
||||
public String(final byte code) {
|
||||
public LZWString(final byte code) {
|
||||
this(code, code, 1, null);
|
||||
}
|
||||
|
||||
private String(final byte value, final byte firstChar, final int length, final String previous) {
|
||||
private LZWString(final byte value, final byte firstChar, final int length, final LZWString previous) {
|
||||
this.value = value;
|
||||
this.firstChar = firstChar;
|
||||
this.length = length;
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
public final String concatenate(final byte firstChar) {
|
||||
return new String(firstChar, this.firstChar, length + 1, this);
|
||||
public final LZWString concatenate(final byte firstChar) {
|
||||
return new LZWString(firstChar, this.firstChar, length + 1, this);
|
||||
}
|
||||
|
||||
public final void writeTo(final ByteBuffer buffer) {
|
||||
@@ -310,7 +316,7 @@ abstract class LZWDecoder implements Decoder {
|
||||
buffer.put(value);
|
||||
}
|
||||
else {
|
||||
String e = this;
|
||||
LZWString e = this;
|
||||
final int offset = buffer.position();
|
||||
|
||||
for (int i = length - 1; i >= 0; i--) {
|
||||
@@ -321,6 +327,46 @@ abstract class LZWDecoder implements Decoder {
|
||||
buffer.position(offset + length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("ZLWString[");
|
||||
int offset = builder.length();
|
||||
LZWString e = this;
|
||||
for (int i = length - 1; i >= 0; i--) {
|
||||
builder.insert(offset, String.format("%2x", e.value));
|
||||
e = e.previous;
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LZWString string = (LZWString) other;
|
||||
|
||||
return firstChar == string.firstChar &&
|
||||
length == string.length &&
|
||||
value == string.value &&
|
||||
// !(previous != null ? !previous.equals(string.previous) : string.previous != null);
|
||||
previous == string.previous;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = previous != null ? previous.hashCode() : 0;
|
||||
result = 31 * result + length;
|
||||
result = 31 * result + (int) value;
|
||||
result = 31 * result + (int) firstChar;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -55,4 +55,8 @@ interface TIFFBaseline {
|
||||
int EXTRASAMPLE_UNASSOCIATED_ALPHA = 2;
|
||||
|
||||
int PREDICTOR_NONE = 1;
|
||||
|
||||
int RESOLUTION_UNIT_NONE = 1;
|
||||
int RESOLUTION_UNIT_DPI = 2; // Default
|
||||
int RESOLUTION_UNIT_CENTIMETER = 3;
|
||||
}
|
||||
|
||||
+2
-2
@@ -54,9 +54,9 @@ interface TIFFCustom {
|
||||
int PHOTOMETRIC_LOGL = 32844;
|
||||
int PHOTOMETRIC_LOGLUV = 32845;
|
||||
|
||||
/** DNG: CFA (Color Filter Array)*/
|
||||
/** DNG: CFA (Color Filter Array). */
|
||||
int PHOTOMETRIC_CFA = 32803;
|
||||
/** DNG: LinearRaw*/
|
||||
/** DNG: LinearRaw. */
|
||||
int PHOTOMETRIC_LINEAR_RAW = 34892;
|
||||
|
||||
int SAMPLEFORMAT_COMPLEX_INT = 5;
|
||||
|
||||
+13
-2
@@ -42,7 +42,7 @@ interface TIFFExtension {
|
||||
int COMPRESSION_CCITT_T6 = 4;
|
||||
/** LZW Compression. Was baseline, but moved to extension due to license issues in the LZW algorithm. */
|
||||
int COMPRESSION_LZW = 5;
|
||||
/** Deprecated. For backwards compatibility only. */
|
||||
/** Deprecated. For backwards compatibility only ("Old-style" JPEG). */
|
||||
int COMPRESSION_OLD_JPEG = 6;
|
||||
/** JPEG Compression (lossy). */
|
||||
int COMPRESSION_JPEG = 7;
|
||||
@@ -69,7 +69,18 @@ interface TIFFExtension {
|
||||
int YCBCR_POSITIONING_CENTERED = 1;
|
||||
int YCBCR_POSITIONING_COSITED = 2;
|
||||
|
||||
// "Old-style" JPEG (obsolete)
|
||||
/** Deprecated. For backwards compatibility only ("Old-style" JPEG). */
|
||||
int JPEG_PROC_BASELINE = 1;
|
||||
/** Deprecated. For backwards compatibility only ("Old-style" JPEG). */
|
||||
int JPEG_PROC_LOSSLESS = 14;
|
||||
|
||||
/** For use with Photometric: 5 (Separated), when image data is in CMYK color space. */
|
||||
int INKSET_CMYK = 1;
|
||||
|
||||
/**
|
||||
* For use with Photometric: 5 (Separated), when image data is in a color space other than CMYK.
|
||||
* See {@link com.twelvemonkeys.imageio.metadata.exif.TIFF#TAG_INK_NAMES InkNames} field for a
|
||||
* description of the inks to be used.
|
||||
*/
|
||||
int INKSET_NOT_CMYK = 2;
|
||||
}
|
||||
|
||||
Regular → Executable
+296
-138
@@ -105,8 +105,6 @@ import java.util.zip.InflaterInputStream;
|
||||
*/
|
||||
public class TIFFImageReader extends ImageReaderBase {
|
||||
// TODOs ImageIO basic functionality:
|
||||
// TODO: Subsampling (*tests should be failing*)
|
||||
// TODO: Source region (*tests should be failing*)
|
||||
// TODO: Thumbnail support
|
||||
// TODO: TIFFImageWriter + Spi
|
||||
|
||||
@@ -134,6 +132,8 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
// Handle SampleFormat (and give up if not == 1)
|
||||
// Support Compression 6 ('Old-style' JPEG)
|
||||
// Support Compression 2 (CCITT Modified Huffman RLE) for bi-level images
|
||||
// Source region
|
||||
// Subsampling
|
||||
|
||||
final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.tiff.debug"));
|
||||
|
||||
@@ -186,7 +186,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
Entry entry = currentIFD.getEntryById(tag);
|
||||
|
||||
if (entry == null) {
|
||||
if (defaultValue != null) {
|
||||
if (defaultValue != null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@@ -258,6 +258,11 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
case 1:
|
||||
// TIFF 6.0 Spec says: 1, 4 or 8 for baseline (1 for bi-level, 4/8 for gray)
|
||||
// ImageTypeSpecifier supports 1, 2, 4, 8 or 16 bits, we'll go with that for now
|
||||
if (profile != null && profile.getColorSpaceType() != ColorSpace.TYPE_GRAY) {
|
||||
processWarningOccurred(String.format("Embedded ICC color profile (type %s), is incompatible with image data (GRAY/type 6). Ignoring profile.", profile.getColorSpaceType()));
|
||||
profile = null;
|
||||
}
|
||||
|
||||
cs = profile == null ? ColorSpace.getInstance(ColorSpace.CS_GRAY) : ColorSpaces.createColorSpace(profile);
|
||||
|
||||
if (cs == ColorSpace.getInstance(ColorSpace.CS_GRAY) && (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4 || bitsPerSample == 8 || bitsPerSample == 16)) {
|
||||
@@ -274,9 +279,14 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
case TIFFExtension.PHOTOMETRIC_YCBCR:
|
||||
// JPEG reader will handle YCbCr to RGB for us, otherwise we'll convert while reading
|
||||
// TODO: Sanity check that we have SamplesPerPixel == 3, BitsPerSample == [8,8,8] and Compression == 1 (none), 5 (LZW), or 6 (JPEG)
|
||||
// TODO: Sanity check that we have SamplesPerPixel == 3, BitsPerSample == [8,8,8] (or [16,16,16]) and Compression == 1 (none), 5 (LZW), or 6 (JPEG)
|
||||
case TIFFBaseline.PHOTOMETRIC_RGB:
|
||||
// RGB
|
||||
if (profile != null && profile.getColorSpaceType() != ColorSpace.TYPE_RGB) {
|
||||
processWarningOccurred(String.format("Embedded ICC color profile (type %s), is incompatible with image data (RGB/type 5). Ignoring profile.", profile.getColorSpaceType()));
|
||||
profile = null;
|
||||
}
|
||||
|
||||
cs = profile == null ? ColorSpace.getInstance(ColorSpace.CS_sRGB) : ColorSpaces.createColorSpace(profile);
|
||||
|
||||
switch (samplesPerPixel) {
|
||||
@@ -330,24 +340,28 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
throw new IIOException("Missing ColorMap for Palette TIFF");
|
||||
}
|
||||
|
||||
int[] cmapShort = (int[]) colorMap.getValue();
|
||||
int[] cmap = new int[colorMap.valueCount() / 3];
|
||||
|
||||
// All reds, then greens, and finally blues
|
||||
for (int i = 0; i < cmap.length; i++) {
|
||||
cmap[i] = (cmapShort[i ] / 256) << 16
|
||||
| (cmapShort[i + cmap.length] / 256) << 8
|
||||
| (cmapShort[i + 2 * cmap.length] / 256);
|
||||
}
|
||||
|
||||
IndexColorModel icm = new IndexColorModel(bitsPerSample, cmap.length, cmap, 0, false, -1, dataType);
|
||||
IndexColorModel icm = createIndexColorModel(bitsPerSample, dataType, (int[]) colorMap.getValue());
|
||||
|
||||
return IndexedImageTypeSpecifier.createFromIndexColorModel(icm);
|
||||
|
||||
case TIFFExtension.PHOTOMETRIC_SEPARATED:
|
||||
// Separated (CMYK etc)
|
||||
// TODO: Consult the 332/InkSet (1=CMYK, 2=Not CMYK; see InkNames), 334/NumberOfInks (def=4) and optionally 333/InkNames
|
||||
// Consult the 332/InkSet (1=CMYK, 2=Not CMYK; see InkNames), 334/NumberOfInks (def=4) and optionally 333/InkNames
|
||||
// If "Not CMYK" we'll need an ICC profile to be able to display (in a useful way), readAsRaster should still work.
|
||||
int inkSet = getValueAsIntWithDefault(TIFF.TAG_INK_SET, TIFFExtension.INKSET_CMYK);
|
||||
int numberOfInks = getValueAsIntWithDefault(TIFF.TAG_NUMBER_OF_INKS, 4);
|
||||
|
||||
// Profile must be CMYK, OR color component must match NumberOfInks
|
||||
if (inkSet != TIFFExtension.INKSET_CMYK && (profile == null || profile.getNumComponents() != numberOfInks)) {
|
||||
throw new IIOException(String.format(
|
||||
"Embedded ICC color profile for Photometric Separated is missing or is incompatible with image data: %s != NumberOfInks (%s).",
|
||||
profile != null ? profile.getNumComponents() : "null", numberOfInks));
|
||||
}
|
||||
if (profile != null && inkSet == TIFFExtension.INKSET_CMYK && profile.getColorSpaceType() != ColorSpace.TYPE_CMYK) {
|
||||
processWarningOccurred(String.format("Embedded ICC color profile (type %s), is incompatible with image data (CMYK/type 9). Ignoring profile.", profile.getColorSpaceType()));
|
||||
profile = null;
|
||||
}
|
||||
|
||||
cs = profile == null ? ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK) : ColorSpaces.createColorSpace(profile);
|
||||
|
||||
switch (samplesPerPixel) {
|
||||
@@ -389,6 +403,43 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private IndexColorModel createIndexColorModel(final int bitsPerSample, final int dataType, final int[] cmapShort) {
|
||||
// According to the spec, there should be exactly 3 * bitsPerSample^2 entries in the color map for TIFF.
|
||||
// Should we enforce this?
|
||||
|
||||
int[] cmap = new int[cmapShort.length / 3];
|
||||
|
||||
// We'll detect whether the color map data is 8 bit, rather than 16 bit while converting
|
||||
boolean cmapIs8Bit = true;
|
||||
|
||||
// All reds, then greens, and finally blues
|
||||
for (int i = 0; i < cmap.length; i++) {
|
||||
cmap[i] = (cmapShort[i ] / 256) << 16
|
||||
| (cmapShort[i + cmap.length] / 256) << 8
|
||||
| (cmapShort[i + 2 * cmap.length] / 256);
|
||||
|
||||
if (cmapIs8Bit && cmap[i] != 0) {
|
||||
cmapIs8Bit = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmapIs8Bit) {
|
||||
// This color map is using only the lower 8 bits, making the image all black.
|
||||
// We'll create a new color map, based on the non-scaled 8 bit values.
|
||||
|
||||
processWarningOccurred("8 bit ColorMap detected.");
|
||||
|
||||
// All reds, then greens, and finally blues
|
||||
for (int i = 0; i < cmap.length; i++) {
|
||||
cmap[i] = (cmapShort[i ]) << 16
|
||||
| (cmapShort[i + cmap.length]) << 8
|
||||
| (cmapShort[i + 2 * cmap.length]);
|
||||
}
|
||||
}
|
||||
|
||||
return new IndexColorModel(bitsPerSample, cmap.length, cmap, 0, false, -1, dataType);
|
||||
}
|
||||
|
||||
private int getSampleFormat() throws IIOException {
|
||||
long[] value = getValueAsLongArray(TIFF.TAG_SAMPLE_FORMAT, "SampleFormat", false);
|
||||
|
||||
@@ -467,17 +518,20 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||
checkReadParamBandSettings(param, rawType.getNumBands(), destination.getSampleModel().getNumBands());
|
||||
|
||||
final Rectangle source = new Rectangle();
|
||||
final Rectangle dest = new Rectangle();
|
||||
computeRegions(param, width, height, destination, source, dest);
|
||||
final Rectangle srcRegion = new Rectangle();
|
||||
final Rectangle dstRegion = new Rectangle();
|
||||
computeRegions(param, width, height, destination, srcRegion, dstRegion);
|
||||
|
||||
WritableRaster raster = destination.getRaster();
|
||||
int xSub = param != null ? param.getSourceXSubsampling() : 1;
|
||||
int ySub = param != null ? param.getSourceYSubsampling() : 1;
|
||||
|
||||
WritableRaster destRaster = clipToRect(destination.getRaster(), dstRegion, param != null ? param.getDestinationBands() : null);
|
||||
|
||||
final int interpretation = getValueAsInt(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, "PhotometricInterpretation");
|
||||
final int compression = getValueAsIntWithDefault(TIFF.TAG_COMPRESSION, TIFFBaseline.COMPRESSION_NONE);
|
||||
final int predictor = getValueAsIntWithDefault(TIFF.TAG_PREDICTOR, 1);
|
||||
final int planarConfiguration = getValueAsIntWithDefault(TIFF.TAG_PLANAR_CONFIGURATION, TIFFBaseline.PLANARCONFIG_CHUNKY);
|
||||
final int numBands = planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR ? 1 : raster.getNumBands();
|
||||
final int numBands = planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR ? 1 : rawType.getNumBands();
|
||||
|
||||
// NOTE: We handle strips as tiles of tileWidth == width by tileHeight == rowsPerStrip
|
||||
// Strips are top/down, tiles are left/right, top/down
|
||||
@@ -540,11 +594,11 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
double[] yCbCrCoefficients = null;
|
||||
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR) {
|
||||
// getRawImageType does the lookup/conversion for these
|
||||
if (raster.getNumBands() != 3) {
|
||||
throw new IIOException("TIFF PhotometricInterpretation YCbCr requires SamplesPerPixel == 3: " + raster.getNumBands());
|
||||
if (rowRaster.getNumBands() != 3) {
|
||||
throw new IIOException("TIFF PhotometricInterpretation YCbCr requires SamplesPerPixel == 3: " + rowRaster.getNumBands());
|
||||
}
|
||||
if (raster.getTransferType() != DataBuffer.TYPE_BYTE) {
|
||||
throw new IIOException("TIFF PhotometricInterpretation YCbCr requires BitsPerSample == [8,8,8]");
|
||||
if (rowRaster.getTransferType() != DataBuffer.TYPE_BYTE && rowRaster.getTransferType() != DataBuffer.TYPE_USHORT) {
|
||||
throw new IIOException("TIFF PhotometricInterpretation YCbCr requires BitsPerSample == [8,8,8] or [16,16,16]");
|
||||
}
|
||||
|
||||
yCbCrPos = getValueAsIntWithDefault(TIFF.TAG_YCBCR_POSITIONING, TIFFExtension.YCBCR_POSITIONING_CENTERED);
|
||||
@@ -590,7 +644,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
// Read data
|
||||
processImageStarted(imageIndex);
|
||||
|
||||
// TODO: Read only tiles that lies within region
|
||||
// General uncompressed/compressed reading
|
||||
for (int y = 0; y < tilesDown; y++) {
|
||||
int col = 0;
|
||||
@@ -612,12 +665,19 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
? IIOUtil.createStreamAdapter(imageInput, stripTileByteCounts[i])
|
||||
: IIOUtil.createStreamAdapter(imageInput);
|
||||
|
||||
adapter = createDecompressorStream(compression, width, adapter);
|
||||
adapter = createUnpredictorStream(predictor, width, planarConfiguration == 2 ? 1 : raster.getNumBands(), getBitsPerSample(), adapter, imageInput.getByteOrder());
|
||||
adapter = createDecompressorStream(compression, stripTileWidth, adapter);
|
||||
adapter = createUnpredictorStream(predictor, stripTileWidth, numBands, getBitsPerSample(), adapter, imageInput.getByteOrder());
|
||||
|
||||
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR) {
|
||||
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR && rowRaster.getTransferType() == DataBuffer.TYPE_BYTE) {
|
||||
adapter = new YCbCrUpsamplerStream(adapter, yCbCrSubsampling, yCbCrPos, colsInTile, yCbCrCoefficients);
|
||||
}
|
||||
else if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR && rowRaster.getTransferType() == DataBuffer.TYPE_USHORT) {
|
||||
adapter = new YCbCr16UpsamplerStream(adapter, yCbCrSubsampling, yCbCrPos, colsInTile, yCbCrCoefficients, imageInput.getByteOrder());
|
||||
}
|
||||
else if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR) {
|
||||
// Handled in getRawImageType
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
// According to the spec, short/long/etc should follow order of containing stream
|
||||
input = imageInput.getByteOrder() == ByteOrder.BIG_ENDIAN
|
||||
@@ -625,8 +685,15 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
: new LittleEndianDataInputStream(adapter);
|
||||
}
|
||||
|
||||
// Clip the stripTile rowRaster to not exceed the srcRegion
|
||||
Rectangle clip = new Rectangle(srcRegion);
|
||||
clip.width = Math.min((colsInTile + xSub - 1) / xSub, srcRegion.width);
|
||||
Raster clippedRow = clipRowToRect(rowRaster, clip,
|
||||
param != null ? param.getSourceBands() : null,
|
||||
param != null ? param.getSourceXSubsampling() : 1);
|
||||
|
||||
// Read a full strip/tile
|
||||
readStripTileData(rowRaster, interpretation, raster, col, row, colsInTile, rowsInTile, input);
|
||||
readStripTileData(clippedRow, srcRegion, xSub, ySub, numBands, interpretation, destRaster, col, row, colsInTile, rowsInTile, input);
|
||||
|
||||
if (abortRequested()) {
|
||||
break;
|
||||
@@ -635,7 +702,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
col += colsInTile;
|
||||
}
|
||||
|
||||
processImageProgress(100f * row / (float) height);
|
||||
processImageProgress(100f * row / height);
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
@@ -661,21 +728,14 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
Entry tablesEntry = currentIFD.getEntryById(TIFF.TAG_JPEG_TABLES);
|
||||
byte[] tablesValue = tablesEntry != null ? (byte[]) tablesEntry.getValue() : null;
|
||||
if (tablesValue != null) {
|
||||
// TODO: Work this out...
|
||||
// Whatever values I pass the reader as the read param, it never gets the same quality as if
|
||||
// I just invoke jpegReader.getStreamMetadata...
|
||||
// Might have something to do with subsampling?
|
||||
// How do we pass the chroma-subsampling parameter from the TIFF structure to the JPEG reader?
|
||||
|
||||
// TODO: Consider splicing the TAG_JPEG_TABLES into the streams for each tile, for a
|
||||
// (slightly slower for multiple images, but) more compatible approach..?
|
||||
|
||||
// I just invoke jpegReader.getStreamMetadata(), so we'll do that...
|
||||
jpegReader.setInput(new ByteArrayImageInputStream(tablesValue));
|
||||
|
||||
// NOTE: This initializes the tables and other internal settings for the reader (as if by magic).
|
||||
// This is actually a feature of JPEG,
|
||||
// see: http://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#abbrev
|
||||
/*IIOMetadata streamMetadata = */jpegReader.getStreamMetadata();
|
||||
// This initializes the tables and other internal settings for the reader,
|
||||
// and is actually a feature of JPEG, see abbreviated streams:
|
||||
// http://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#abbrev
|
||||
jpegReader.getStreamMetadata();
|
||||
}
|
||||
else {
|
||||
processWarningOccurred("Missing JPEGTables for TIFF with compression: 7 (JPEG)");
|
||||
@@ -683,7 +743,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
// Read data
|
||||
processImageStarted(imageIndex);
|
||||
processImageStarted(imageIndex); // Better yet, would be to delegate read progress here...
|
||||
|
||||
for (int y = 0; y < tilesDown; y++) {
|
||||
int col = 0;
|
||||
@@ -693,20 +753,24 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
int i = y * tilesAcross + x;
|
||||
int colsInTile = Math.min(stripTileWidth, width - col);
|
||||
|
||||
imageInput.seek(stripTileOffsets[i]);
|
||||
ImageInputStream subStream = new SubImageInputStream(imageInput, stripTileByteCounts != null ? (int) stripTileByteCounts[i] : Short.MAX_VALUE);
|
||||
// Read only tiles that lies within region
|
||||
if (new Rectangle(col, row, colsInTile, rowsInTile).intersects(srcRegion)) {
|
||||
imageInput.seek(stripTileOffsets[i]);
|
||||
ImageInputStream subStream = new SubImageInputStream(imageInput, stripTileByteCounts != null ? (int) stripTileByteCounts[i] : Short.MAX_VALUE);
|
||||
|
||||
try {
|
||||
jpegReader.setInput(subStream);
|
||||
jpegParam.setSourceRegion(new Rectangle(0, 0, colsInTile, rowsInTile));
|
||||
jpegParam.setDestinationOffset(new Point(col - srcRegion.x, row - srcRegion.y));
|
||||
jpegParam.setDestination(destination);
|
||||
// TODO: This works only if Gray/YCbCr/RGB, not CMYK/LAB/etc...
|
||||
// In the latter case we will have to use readAsRaster and do color conversion ourselves
|
||||
jpegReader.read(0, jpegParam);
|
||||
}
|
||||
finally {
|
||||
subStream.close();
|
||||
}
|
||||
|
||||
try {
|
||||
jpegReader.setInput(subStream);
|
||||
jpegParam.setSourceRegion(new Rectangle(0, 0, colsInTile, rowsInTile));
|
||||
jpegParam.setDestinationOffset(new Point(col, row));
|
||||
jpegParam.setDestination(destination);
|
||||
// TODO: This works only if Gray/YCbCr/RGB, not CMYK/LAB/etc...
|
||||
// In the latter case we will have to use readAsRaster and do color conversion ourselves
|
||||
jpegReader.read(0, jpegParam);
|
||||
}
|
||||
finally {
|
||||
subStream.close();
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
@@ -716,7 +780,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
col += colsInTile;
|
||||
}
|
||||
|
||||
processImageProgress(100f * row / (float) height);
|
||||
processImageProgress(100f * row / height);
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
@@ -755,7 +819,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
int jpegLenght = getValueAsIntWithDefault(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, -1);
|
||||
// TODO: 515/JPEGRestartInterval (may be absent)
|
||||
|
||||
// Currently ignored
|
||||
// Currently ignored (for lossless only)
|
||||
// 517/JPEGLosslessPredictors
|
||||
// 518/JPEGPointTransforms
|
||||
|
||||
@@ -763,8 +827,9 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
if (jpegOffset != -1) {
|
||||
// Straight forward case: We're good to go! We'll disregard tiling and any tables tags
|
||||
|
||||
if (currentIFD.getEntryById(TIFF.TAG_OLD_JPEG_Q_TABLES) != null || currentIFD.getEntryById(TIFF.TAG_OLD_JPEG_DC_TABLES) != null || currentIFD.getEntryById(TIFF.TAG_OLD_JPEG_AC_TABLES) != null) {
|
||||
if (currentIFD.getEntryById(TIFF.TAG_OLD_JPEG_Q_TABLES) != null
|
||||
|| currentIFD.getEntryById(TIFF.TAG_OLD_JPEG_DC_TABLES) != null
|
||||
|| currentIFD.getEntryById(TIFF.TAG_OLD_JPEG_AC_TABLES) != null) {
|
||||
processWarningOccurred("Old-style JPEG compressed TIFF with JFIF stream encountered. Ignoring JPEG tables. Reading as single tile.");
|
||||
}
|
||||
else {
|
||||
@@ -772,11 +837,38 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
imageInput.seek(jpegOffset);
|
||||
|
||||
// NOTE: Some known TIFF encoder encodes bad JPEGInterchangeFormat tags,
|
||||
// but has the correct offset to the JPEG stream in the StripOffsets tag.
|
||||
long realJPEGOffset = jpegOffset;
|
||||
|
||||
short expectedSOI = (short) (imageInput.readByte() << 8 | imageInput.readByte());
|
||||
if (expectedSOI != (short) JPEG.SOI) {
|
||||
if (stripTileOffsets != null && stripTileOffsets.length == 1) {
|
||||
imageInput.seek(stripTileOffsets[0]);
|
||||
|
||||
expectedSOI = (short) (imageInput.readByte() << 8 | imageInput.readByte());
|
||||
if (expectedSOI == (short) JPEG.SOI) {
|
||||
realJPEGOffset = stripTileOffsets[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (realJPEGOffset != jpegOffset) {
|
||||
processWarningOccurred("Incorrect JPEGInterchangeFormat tag, using StripOffsets/TileOffsets instead.");
|
||||
}
|
||||
else {
|
||||
processWarningOccurred("Incorrect JPEGInterchangeFormat tag encountered (not a valid SOI marker).");
|
||||
// We'll fail below, but we don't need to handle this especially
|
||||
}
|
||||
}
|
||||
|
||||
imageInput.seek(realJPEGOffset);
|
||||
|
||||
stream = new SubImageInputStream(imageInput, jpegLenght != -1 ? jpegLenght : Short.MAX_VALUE);
|
||||
jpegReader.setInput(stream);
|
||||
|
||||
// Read data
|
||||
processImageStarted(imageIndex);
|
||||
processImageStarted(imageIndex); // Better yet, would be to delegate read progress here...
|
||||
|
||||
try {
|
||||
jpegParam.setSourceRegion(new Rectangle(0, 0, width, height));
|
||||
@@ -797,7 +889,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
else {
|
||||
// The hard way: Read tables and re-create a full JFIF stream
|
||||
|
||||
processWarningOccurred("Old-style JPEG compressed TIFF without JFIF stream encountered. Attempting to re-create JFIF stream.");
|
||||
|
||||
// 519/JPEGQTables
|
||||
@@ -818,7 +909,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
// seem to contain a useful JPEGInterchangeFormat tag. Therefore, we recommend a careful attempt to read
|
||||
// the Tables tags only as a last resort, if no table data is found in a JPEGInterchangeFormat stream.
|
||||
|
||||
|
||||
// TODO: If any of the q/dc/ac tables are equal (or have same offset, even if "spec" violation),
|
||||
// use only the first occurrence, and update selectors in SOF0 and SOS
|
||||
|
||||
@@ -860,27 +950,30 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
int colsInTile = Math.min(stripTileWidth, width - col);
|
||||
int i = y * tilesAcross + x;
|
||||
|
||||
imageInput.seek(stripTileOffsets[i]);
|
||||
stream = ImageIO.createImageInputStream(new SequenceInputStream(Collections.enumeration(
|
||||
Arrays.asList(
|
||||
createJFIFStream(raster, stripTileWidth, stripTileHeight, qTables, dcTables, acTables),
|
||||
IIOUtil.createStreamAdapter(imageInput, stripTileByteCounts != null ? (int) stripTileByteCounts[i] : Short.MAX_VALUE),
|
||||
new ByteArrayInputStream(new byte[] {(byte) 0xff, (byte) 0xd9}) // EOI
|
||||
)
|
||||
)));
|
||||
// Read only tiles that lies within region
|
||||
if (new Rectangle(col, row, colsInTile, rowsInTile).intersects(srcRegion)) {
|
||||
imageInput.seek(stripTileOffsets[i]);
|
||||
stream = ImageIO.createImageInputStream(new SequenceInputStream(Collections.enumeration(
|
||||
Arrays.asList(
|
||||
createJFIFStream(destRaster, stripTileWidth, stripTileHeight, qTables, dcTables, acTables),
|
||||
IIOUtil.createStreamAdapter(imageInput, stripTileByteCounts != null ? (int) stripTileByteCounts[i] : Short.MAX_VALUE),
|
||||
new ByteArrayInputStream(new byte[] {(byte) 0xff, (byte) 0xd9}) // EOI
|
||||
)
|
||||
)));
|
||||
|
||||
jpegReader.setInput(stream);
|
||||
jpegReader.setInput(stream);
|
||||
|
||||
try {
|
||||
jpegParam.setSourceRegion(new Rectangle(0, 0, colsInTile, rowsInTile));
|
||||
jpegParam.setDestinationOffset(new Point(col, row));
|
||||
jpegParam.setDestination(destination);
|
||||
// TODO: This works only if Gray/YCbCr/RGB, not CMYK/LAB/etc...
|
||||
// In the latter case we will have to use readAsRaster and do color conversion ourselves
|
||||
jpegReader.read(0, jpegParam);
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
try {
|
||||
jpegParam.setSourceRegion(new Rectangle(0, 0, colsInTile, rowsInTile));
|
||||
jpegParam.setDestinationOffset(new Point(col - srcRegion.x, row - srcRegion.y));
|
||||
jpegParam.setDestination(destination);
|
||||
// TODO: This works only if Gray/YCbCr/RGB, not CMYK/LAB/etc...
|
||||
// In the latter case we will have to use readAsRaster and do color conversion ourselves
|
||||
jpegReader.read(0, jpegParam);
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
@@ -890,7 +983,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
col += colsInTile;
|
||||
}
|
||||
|
||||
processImageProgress(100f * row / (float) height);
|
||||
processImageProgress(100f * row / height);
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
@@ -938,10 +1031,10 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
private static InputStream createJFIFStream(WritableRaster raster, int stripTileWidth, int stripTileHeight, byte[][] qTables, byte[][] dcTables, byte[][] acTables) throws IOException {
|
||||
FastByteArrayOutputStream stream = new FastByteArrayOutputStream(
|
||||
2 + 2 + 2 + 6 + 3 * raster.getNumBands() +
|
||||
5 * qTables.length + qTables.length * qTables[0].length +
|
||||
5 * dcTables.length + dcTables.length * dcTables[0].length +
|
||||
5 * acTables.length + acTables.length * acTables[0].length +
|
||||
8 + 2 * raster.getNumBands()
|
||||
5 * qTables.length + qTables.length * qTables[0].length +
|
||||
5 * dcTables.length + dcTables.length * dcTables[0].length +
|
||||
5 * acTables.length + acTables.length * acTables[0].length +
|
||||
8 + 2 * raster.getNumBands()
|
||||
);
|
||||
|
||||
DataOutputStream out = new DataOutputStream(stream);
|
||||
@@ -1004,81 +1097,111 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
return stream.createInputStream();
|
||||
}
|
||||
|
||||
private void readStripTileData(final WritableRaster rowRaster, final int interpretation,
|
||||
final WritableRaster raster, final int col, final int startRow,
|
||||
final int colsInStrip, final int rowsInStrip, final DataInput input)
|
||||
private Raster clipRowToRect(final Raster raster, final Rectangle rect, final int[] bands, final int xSub) {
|
||||
if (rect.contains(raster.getMinX(), 0, raster.getWidth(), 1)
|
||||
&& xSub == 1
|
||||
&& bands == null /* TODO: Compare bands with that of raster */) {
|
||||
return raster;
|
||||
}
|
||||
|
||||
return raster.createChild(rect.x / xSub, 0, rect.width / xSub, 1, 0, 0, bands);
|
||||
}
|
||||
|
||||
private WritableRaster clipToRect(final WritableRaster raster, final Rectangle rect, final int[] bands) {
|
||||
if (rect.contains(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight())
|
||||
&& bands == null /* TODO: Compare bands with that of raster */) {
|
||||
return raster;
|
||||
}
|
||||
|
||||
return raster.createWritableChild(rect.x, rect.y, rect.width, rect.height, 0, 0, bands);
|
||||
}
|
||||
|
||||
private void readStripTileData(final Raster tileRowRaster, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||
final int numBands, final int interpretation,
|
||||
final WritableRaster raster, final int startCol, final int startRow,
|
||||
final int colsInTile, final int rowsInTile, final DataInput input)
|
||||
throws IOException {
|
||||
switch (rowRaster.getTransferType()) {
|
||||
|
||||
switch (tileRowRaster.getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
byte[] rowData = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||
byte[] rowDataByte = ((DataBufferByte) tileRowRaster.getDataBuffer()).getData();
|
||||
|
||||
for (int j = 0; j < rowsInStrip; j++) {
|
||||
int row = startRow + j;
|
||||
|
||||
if (row >= raster.getHeight()) {
|
||||
break;
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
|
||||
input.readFully(rowData);
|
||||
normalizeBlack(interpretation, rowData);
|
||||
input.readFully(rowDataByte);
|
||||
|
||||
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
|
||||
raster.setDataElements(col, row, rowRaster);
|
||||
}
|
||||
else if (col >= raster.getMinX() && col < raster.getWidth()) {
|
||||
raster.setDataElements(col, row, rowRaster.createChild(0, 0, Math.min(colsInStrip, raster.getWidth() - col), 1, 0, 0, null));
|
||||
if (row % ySub == 0 && row >= srcRegion.y) {
|
||||
normalizeBlack(interpretation, rowDataByte);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
for (int b = 0; b < numBands; b++) {
|
||||
rowDataByte[x + b] = rowDataByte[x * xSub + b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raster.setDataElements(startCol, (row - srcRegion.y) / ySub, tileRowRaster);
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
|
||||
break;
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
short [] rowDataShort = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
|
||||
short[] rowDataShort = ((DataBufferUShort) tileRowRaster.getDataBuffer()).getData();
|
||||
|
||||
for (int j = 0; j < rowsInStrip; j++) {
|
||||
int row = startRow + j;
|
||||
|
||||
if (row >= raster.getHeight()) {
|
||||
break;
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
|
||||
for (int k = 0; k < rowDataShort.length; k++) {
|
||||
rowDataShort[k] = input.readShort();
|
||||
}
|
||||
readFully(input, rowDataShort);
|
||||
|
||||
normalizeBlack(interpretation, rowDataShort);
|
||||
if (row >= srcRegion.y) {
|
||||
normalizeBlack(interpretation, rowDataShort);
|
||||
|
||||
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
|
||||
raster.setDataElements(col, row, rowRaster);
|
||||
}
|
||||
else if (col >= raster.getMinX() && col < raster.getWidth()) {
|
||||
raster.setDataElements(col, row, rowRaster.createChild(0, 0, Math.min(colsInStrip, raster.getWidth() - col), 1, 0, 0, null));
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
for (int b = 0; b < numBands; b++) {
|
||||
rowDataShort[x + b] = rowDataShort[x * xSub + b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raster.setDataElements(startCol, row - srcRegion.y, tileRowRaster);
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
|
||||
break;
|
||||
case DataBuffer.TYPE_INT:
|
||||
int [] rowDataInt = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
|
||||
int[] rowDataInt = ((DataBufferInt) tileRowRaster.getDataBuffer()).getData();
|
||||
|
||||
for (int j = 0; j < rowsInStrip; j++) {
|
||||
int row = startRow + j;
|
||||
|
||||
if (row >= raster.getHeight()) {
|
||||
break;
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
|
||||
for (int k = 0; k < rowDataInt.length; k++) {
|
||||
rowDataInt[k] = input.readInt();
|
||||
}
|
||||
readFully(input, rowDataInt);
|
||||
|
||||
normalizeBlack(interpretation, rowDataInt);
|
||||
if (row >= srcRegion.y) {
|
||||
normalizeBlack(interpretation, rowDataInt);
|
||||
|
||||
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
|
||||
raster.setDataElements(col, row, rowRaster);
|
||||
}
|
||||
else if (col >= raster.getMinX() && col < raster.getWidth()) {
|
||||
raster.setDataElements(col, row, rowRaster.createChild(0, 0, Math.min(colsInStrip, raster.getWidth() - col), 1, 0, 0, null));
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
for (int b = 0; b < numBands; b++) {
|
||||
rowDataInt[x + b] = rowDataInt[x * xSub + b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raster.setDataElements(startCol, row - srcRegion.y, tileRowRaster);
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
@@ -1087,6 +1210,32 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Candidate util method (with off/len + possibly byte order)
|
||||
private void readFully(final DataInput input, final int[] rowDataInt) throws IOException {
|
||||
if (input instanceof ImageInputStream) {
|
||||
ImageInputStream imageInputStream = (ImageInputStream) input;
|
||||
imageInputStream.readFully(rowDataInt, 0, rowDataInt.length);
|
||||
}
|
||||
else {
|
||||
for (int k = 0; k < rowDataInt.length; k++) {
|
||||
rowDataInt[k] = input.readInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Candidate util method (with off/len + possibly byte order)
|
||||
private void readFully(final DataInput input, final short[] rowDataShort) throws IOException {
|
||||
if (input instanceof ImageInputStream) {
|
||||
ImageInputStream imageInputStream = (ImageInputStream) input;
|
||||
imageInputStream.readFully(rowDataShort, 0, rowDataShort.length);
|
||||
}
|
||||
else {
|
||||
for (int k = 0; k < rowDataShort.length; k++) {
|
||||
rowDataShort[k] = input.readShort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizeBlack(int photometricInterpretation, short[] data) {
|
||||
if (photometricInterpretation == TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO) {
|
||||
// Inverse values
|
||||
@@ -1121,7 +1270,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
case TIFFBaseline.COMPRESSION_PACKBITS:
|
||||
return new DecoderStream(stream, new PackBitsDecoder(), 1024);
|
||||
case TIFFExtension.COMPRESSION_LZW:
|
||||
return new DecoderStream(stream, LZWDecoder.create(LZWDecoder.isOldBitReversedStream(stream)), 1024);
|
||||
return new DecoderStream(stream, LZWDecoder.create(LZWDecoder.isOldBitReversedStream(stream)), width);
|
||||
case TIFFExtension.COMPRESSION_ZLIB:
|
||||
// TIFFphotoshop.pdf (aka TIFF specification, supplement 2) says ZLIB (8) and DEFLATE (32946) algorithms are identical
|
||||
case TIFFExtension.COMPRESSION_DEFLATE:
|
||||
@@ -1280,13 +1429,16 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
// }
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
// param.setSourceRegion(new Rectangle(100, 100, 100, 100));
|
||||
// int width = reader.getWidth(imageNo);
|
||||
// int height = reader.getHeight(imageNo);
|
||||
// param.setSourceRegion(new Rectangle(width / 4, height / 4, width / 2, height / 2));
|
||||
// param.setSourceRegion(new Rectangle(100, 300, 400, 400));
|
||||
// param.setDestinationOffset(new Point(50, 150));
|
||||
// param.setSourceSubsampling(2, 2, 0, 0);
|
||||
BufferedImage image = reader.read(imageNo, param);
|
||||
System.err.println("Read time: " + (System.currentTimeMillis() - start) + " ms");
|
||||
|
||||
IIOMetadata metadata = reader.getImageMetadata(0);
|
||||
IIOMetadata metadata = reader.getImageMetadata(imageNo);
|
||||
if (metadata != null) {
|
||||
new XMLSerializer(System.out, "UTF-8").serialize(metadata.getAsTree(metadata.getNativeMetadataFormatName()), false);
|
||||
}
|
||||
@@ -1324,6 +1476,12 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
// // System.err.println("Scale time: " + (System.currentTimeMillis() - start) + " ms");
|
||||
// }
|
||||
|
||||
if (image.getType() == BufferedImage.TYPE_CUSTOM) {
|
||||
start = System.currentTimeMillis();
|
||||
image = new ColorConvertOp(null).filter(image, new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB));
|
||||
System.err.println("Conversion time: " + (System.currentTimeMillis() - start) + " ms");
|
||||
}
|
||||
|
||||
showIt(image, String.format("Image: %s [%d x %d]", file.getName(), reader.getWidth(imageNo), reader.getHeight(imageNo)));
|
||||
|
||||
try {
|
||||
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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.imageio.plugins.tiff;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* Input stream that provides on-the-fly conversion and upsampling of TIFF subsampled YCbCr 16 bit samples
|
||||
* to (raw) RGB 16 bit samples.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: YCbCrUpsamplerStream.java,v 1.0 31.01.13 09:25 haraldk Exp$
|
||||
*/
|
||||
final class YCbCr16UpsamplerStream extends FilterInputStream {
|
||||
// TODO: As we deal with short/16 bit samples, we need to take byte order into account
|
||||
private final int horizChromaSub;
|
||||
private final int vertChromaSub;
|
||||
private final int yCbCrPos;
|
||||
private final int columns;
|
||||
private final double[] coefficients;
|
||||
private final ByteOrder byteOrder;
|
||||
|
||||
private final int units;
|
||||
private final int unitSize;
|
||||
private final int padding;
|
||||
private final byte[] decodedRows;
|
||||
|
||||
int decodedLength;
|
||||
int decodedPos;
|
||||
|
||||
private final byte[] buffer;
|
||||
int bufferLength;
|
||||
int bufferPos;
|
||||
|
||||
public YCbCr16UpsamplerStream(final InputStream stream, final int[] chromaSub, final int yCbCrPos, final int columns, final double[] coefficients, final ByteOrder byteOrder) {
|
||||
super(Validate.notNull(stream, "stream"));
|
||||
|
||||
Validate.notNull(chromaSub, "chromaSub");
|
||||
Validate.isTrue(chromaSub.length == 2, "chromaSub.length != 2");
|
||||
Validate.notNull(byteOrder, "byteOrder");
|
||||
|
||||
this.horizChromaSub = chromaSub[0];
|
||||
this.vertChromaSub = chromaSub[1];
|
||||
this.yCbCrPos = yCbCrPos;
|
||||
this.columns = columns;
|
||||
this.coefficients = coefficients == null ? YCbCrUpsamplerStream.CCIR_601_1_COEFFICIENTS : coefficients;
|
||||
this.byteOrder = byteOrder;
|
||||
|
||||
// In TIFF, subsampled streams are stored in "units" of horiz * vert pixels.
|
||||
// For a 4:2 subsampled stream like this:
|
||||
//
|
||||
// Y0 Y1 Y2 Y3 Cb0 Cr0 Y8 Y9 Y10 Y11 Cb1 Cr1
|
||||
// Y4 Y5 Y6 Y7 Y12Y13Y14 Y15
|
||||
//
|
||||
// In the stream, the order is: Y0,Y1,Y2..Y7,Cb0,Cr0, Y8...Y15,Cb1,Cr1, Y16...
|
||||
|
||||
unitSize = 2 * (horizChromaSub * vertChromaSub + 2);
|
||||
units = (columns + horizChromaSub - 1) / horizChromaSub; // If columns % horizChromasSub != 0...
|
||||
padding = 2 * (units * horizChromaSub - columns); // ...each coded row will be padded to fill unit
|
||||
|
||||
decodedRows = new byte[2 * columns * vertChromaSub * 3];
|
||||
buffer = new byte[unitSize * units];
|
||||
}
|
||||
|
||||
private void fetch() throws IOException {
|
||||
if (bufferPos >= bufferLength) {
|
||||
int pos = 0;
|
||||
int read;
|
||||
|
||||
// This *SHOULD* read an entire row of units into the buffer, otherwise decodeRows will throw EOFException
|
||||
while (pos < buffer.length && (read = in.read(buffer, pos, buffer.length - pos)) > 0) {
|
||||
pos += read;
|
||||
}
|
||||
|
||||
bufferLength = pos;
|
||||
bufferPos = 0;
|
||||
}
|
||||
|
||||
if (bufferLength > 0) {
|
||||
decodeRows();
|
||||
}
|
||||
else {
|
||||
decodedLength = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void decodeRows() throws EOFException {
|
||||
decodedLength = decodedRows.length;
|
||||
|
||||
for (int u = 0; u < units; u++) {
|
||||
if (bufferPos >= bufferLength) {
|
||||
throw new EOFException("Unexpected end of stream");
|
||||
}
|
||||
|
||||
// Decode one unit
|
||||
byte cb1 = buffer[bufferPos + unitSize - 4];
|
||||
byte cb2 = buffer[bufferPos + unitSize - 3];
|
||||
byte cr1 = buffer[bufferPos + unitSize - 2];
|
||||
byte cr2 = buffer[bufferPos + unitSize - 1];
|
||||
|
||||
for (int y = 0; y < vertChromaSub; y++) {
|
||||
for (int x = 0; x < horizChromaSub; x++) {
|
||||
// Skip padding at end of row
|
||||
int column = horizChromaSub * u + x;
|
||||
if (column >= columns) {
|
||||
bufferPos += padding;
|
||||
break;
|
||||
}
|
||||
|
||||
int pixelOff = 2 * 3 * (column + columns * y);
|
||||
|
||||
decodedRows[pixelOff ] = buffer[bufferPos++];
|
||||
decodedRows[pixelOff + 1] = buffer[bufferPos++];
|
||||
decodedRows[pixelOff + 2] = cb1;
|
||||
decodedRows[pixelOff + 3] = cb2;
|
||||
decodedRows[pixelOff + 4] = cr1;
|
||||
decodedRows[pixelOff + 5] = cr2;
|
||||
|
||||
// Convert to RGB
|
||||
convertYCbCr2RGB(decodedRows, decodedRows, coefficients, pixelOff);
|
||||
}
|
||||
}
|
||||
|
||||
bufferPos += 2 * 2; // Skip CbCr bytes at end of unit
|
||||
}
|
||||
|
||||
bufferPos = bufferLength;
|
||||
decodedPos = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (decodedLength < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decodedPos >= decodedLength) {
|
||||
fetch();
|
||||
|
||||
if (decodedLength < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return decodedRows[decodedPos++] & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (decodedLength < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decodedPos >= decodedLength) {
|
||||
fetch();
|
||||
|
||||
if (decodedLength < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int read = Math.min(decodedLength - decodedPos, len);
|
||||
System.arraycopy(decodedRows, decodedPos, b, off, read);
|
||||
decodedPos += read;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
if (decodedLength < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decodedPos >= decodedLength) {
|
||||
fetch();
|
||||
|
||||
if (decodedLength < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int skipped = (int) Math.min(decodedLength - decodedPos, n);
|
||||
decodedPos += skipped;
|
||||
|
||||
return skipped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
throw new IOException("mark/reset not supported");
|
||||
}
|
||||
|
||||
private void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final double[] coefficients, final int offset) {
|
||||
int y;
|
||||
int cb;
|
||||
int cr;
|
||||
|
||||
// Short values, depends on byte order!
|
||||
if (byteOrder == ByteOrder.BIG_ENDIAN) {
|
||||
y = ((yCbCr[offset ] & 0xff) << 8) | (yCbCr[offset + 1] & 0xff);
|
||||
cb = (((yCbCr[offset + 2] & 0xff) << 8) | (yCbCr[offset + 3] & 0xff)) - 32768;
|
||||
cr = (((yCbCr[offset + 4] & 0xff) << 8) | (yCbCr[offset + 5] & 0xff)) - 32768;
|
||||
}
|
||||
else {
|
||||
y = ((yCbCr[offset + 1] & 0xff) << 8) | (yCbCr[offset ] & 0xff);
|
||||
cb = (((yCbCr[offset + 3] & 0xff) << 8) | (yCbCr[offset + 2] & 0xff)) - 32768;
|
||||
cr = (((yCbCr[offset + 5] & 0xff) << 8) | (yCbCr[offset + 4] & 0xff)) - 32768;
|
||||
}
|
||||
|
||||
double lumaRed = coefficients[0];
|
||||
double lumaGreen = coefficients[1];
|
||||
double lumaBlue = coefficients[2];
|
||||
|
||||
int red = (int) Math.round(cr * (2 - 2 * lumaRed) + y);
|
||||
int blue = (int) Math.round(cb * (2 - 2 * lumaBlue) + y);
|
||||
int green = (int) Math.round((y - lumaRed * (red) - lumaBlue * (blue)) / lumaGreen);
|
||||
|
||||
short r = clampShort(red);
|
||||
short g = clampShort(green);
|
||||
short b = clampShort(blue);
|
||||
|
||||
// Short values, depends on byte order!
|
||||
if (byteOrder == ByteOrder.BIG_ENDIAN) {
|
||||
rgb[offset ] = (byte) ((r >>> 8) & 0xff);
|
||||
rgb[offset + 1] = (byte) (r & 0xff);
|
||||
rgb[offset + 2] = (byte) ((g >>> 8) & 0xff);
|
||||
rgb[offset + 3] = (byte) (g & 0xff);
|
||||
rgb[offset + 4] = (byte) ((b >>> 8) & 0xff);
|
||||
rgb[offset + 5] = (byte) (b & 0xff);
|
||||
}
|
||||
else {
|
||||
rgb[offset ] = (byte) (r & 0xff);
|
||||
rgb[offset + 1] = (byte) ((r >>> 8) & 0xff);
|
||||
rgb[offset + 2] = (byte) (g & 0xff);
|
||||
rgb[offset + 3] = (byte) ((g >>> 8) & 0xff);
|
||||
rgb[offset + 4] = (byte) (b & 0xff);
|
||||
rgb[offset + 5] = (byte) ((b >>> 8) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
private short clampShort(int val) {
|
||||
return (short) Math.max(0, Math.min(0xffff, val));
|
||||
}
|
||||
}
|
||||
+3
-8
@@ -39,7 +39,7 @@ import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Input stream that provides on-the-fly conversion and upsampling of TIFF susampled YCbCr samples to (raw) RGB samples.
|
||||
* Input stream that provides on-the-fly conversion and upsampling of TIFF subsampled YCbCr samples to (raw) RGB samples.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
@@ -229,7 +229,7 @@ final class YCbCrUpsamplerStream extends FilterInputStream {
|
||||
|
||||
private void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final double[] coefficients, final int offset) {
|
||||
double y = (yCbCr[offset ] & 0xff);
|
||||
double cb = (yCbCr[offset + 1] & 0xff) - 128; // TODO: The -128 part seems bogus... Consult ReferenceBlackWhite??? But default to these values?
|
||||
double cb = (yCbCr[offset + 1] & 0xff) - 128;
|
||||
double cr = (yCbCr[offset + 2] & 0xff) - 128;
|
||||
|
||||
double lumaRed = coefficients[0];
|
||||
@@ -238,7 +238,7 @@ final class YCbCrUpsamplerStream extends FilterInputStream {
|
||||
|
||||
int red = (int) Math.round(cr * (2 - 2 * lumaRed) + y);
|
||||
int blue = (int) Math.round(cb * (2 - 2 * lumaBlue) + y);
|
||||
int green = (int) Math.round((y - lumaRed * (rgb[offset] & 0xff) - lumaBlue * (rgb[offset + 2] & 0xff)) / lumaGreen);
|
||||
int green = (int) Math.round((y - lumaRed * red - lumaBlue * blue) / lumaGreen);
|
||||
|
||||
rgb[offset ] = clamp(red);
|
||||
rgb[offset + 2] = clamp(blue);
|
||||
@@ -342,10 +342,5 @@ final class YCbCrUpsamplerStream extends FilterInputStream {
|
||||
cmyk[offset + 2] = clamp(cmykY);
|
||||
cmyk[offset + 3] = (byte) k; // K passes through unchanged
|
||||
}
|
||||
|
||||
// private static byte clamp(int val) {
|
||||
// return (byte) Math.max(0, Math.min(255, val));
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+110
-1
@@ -27,12 +27,28 @@ package com.twelvemonkeys.imageio.plugins.tiff;/*
|
||||
*/
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.contains;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* TIFFImageReaderTest
|
||||
*
|
||||
@@ -60,7 +76,9 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTestCase<TIFFImageRe
|
||||
new TestData(getClassLoaderResource("/tiff/ycbcr-cat.tif"), new Dimension(250, 325)), // YCbCr, LZW compressed
|
||||
new TestData(getClassLoaderResource("/tiff/quad-jpeg.tif"), new Dimension(512, 384)), // YCbCr, JPEG compressed, striped
|
||||
new TestData(getClassLoaderResource("/tiff/smallliz.tif"), new Dimension(160, 160)), // YCbCr, Old-Style JPEG compressed (full JFIF stream)
|
||||
new TestData(getClassLoaderResource("/tiff/zackthecat.tif"), new Dimension(234, 213)) // YCbCr, Old-Style JPEG compressed (tables, no JFIF stream)
|
||||
new TestData(getClassLoaderResource("/tiff/zackthecat.tif"), new Dimension(234, 213)), // YCbCr, Old-Style JPEG compressed (tables, no JFIF stream)
|
||||
new TestData(getClassLoaderResource("/tiff/test-single-gray-compression-type-2.tiff"), new Dimension(1728, 1146)), // Gray, CCITT type 2 compressed
|
||||
new TestData(getClassLoaderResource("/tiff/cramps-tile.tif"), new Dimension(800, 607)) // Gray/WhiteIsZero, uncompressed, striped & tiled...
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,5 +112,96 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTestCase<TIFFImageRe
|
||||
return Arrays.asList("image/tiff");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testReadWithSourceRegionParamEqualImageTiled() throws IOException {
|
||||
assertReadWithSourceRegionParamEqualImage(
|
||||
new Rectangle(23, 23, 15, 15),
|
||||
new TestData(getClassLoaderResource("/tiff/sm_colors_pb_tile.tif"), new Dimension(64, 64)),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Should test USHORT & INT datatypes
|
||||
|
||||
@Test
|
||||
public void testReadWithSourceRegionParamEqualImageJPEG() throws IOException {
|
||||
// The tiles are 512 x 16, make sure we read across tiles
|
||||
assertReadWithSourceRegionParamEqualImage(
|
||||
new Rectangle(71, 71, 17, 21),
|
||||
new TestData(getClassLoaderResource("/tiff/quad-jpeg.tif"), new Dimension(512, 384)),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Test YCbCr colors
|
||||
|
||||
@Test
|
||||
public void testReadOldStyleJPEGGrayscale() throws IOException {
|
||||
TestData testData = new TestData(getClassLoaderResource("/tiff/grayscale-old-style-jpeg.tiff"), new Dimension(600, 600));
|
||||
ImageInputStream stream = testData.getInputStream();
|
||||
|
||||
try {
|
||||
TIFFImageReader reader = createReader();
|
||||
reader.setInput(stream);
|
||||
BufferedImage image = reader.read(0);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(testData.getDimension(0), new Dimension(image.getWidth(), image.getHeight()));
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadIncompatibleICCProfileIgnoredWithWarning() throws IOException {
|
||||
TestData testData = new TestData(getClassLoaderResource("/tiff/rgb-with-embedded-cmyk-icc.tif"), new Dimension(1500, 1500));
|
||||
|
||||
ImageInputStream stream = testData.getInputStream();
|
||||
|
||||
try {
|
||||
TIFFImageReader reader = createReader();
|
||||
reader.setInput(stream);
|
||||
|
||||
IIOReadWarningListener warningListener = mock(IIOReadWarningListener.class);
|
||||
reader.addIIOReadWarningListener(warningListener);
|
||||
|
||||
BufferedImage image = reader.read(0);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(testData.getDimension(0), new Dimension(image.getWidth(), image.getHeight()));
|
||||
verify(warningListener, atLeastOnce()).warningOccurred(eq(reader), contains("ICC"));
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testColorMap8Bit() throws IOException {
|
||||
TestData testData = new TestData(getClassLoaderResource("/tiff/scan-lzw-8bit-colormap.tiff"), new Dimension(2550, 3300));
|
||||
|
||||
ImageInputStream stream = testData.getInputStream();
|
||||
|
||||
try {
|
||||
TIFFImageReader reader = createReader();
|
||||
reader.setInput(stream);
|
||||
|
||||
IIOReadWarningListener warningListener = mock(IIOReadWarningListener.class);
|
||||
reader.addIIOReadWarningListener(warningListener);
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(8, 8));
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(new Dimension(8, 8), new Dimension(image.getWidth(), image.getHeight()));
|
||||
assertEquals(0xffffffff, image.getRGB(0, 0)); // The pixel at 0, 0 should be white, not black
|
||||
verify(warningListener, atLeastOnce()).warningOccurred(eq(reader), contains("ColorMap"));
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.imageio.plugins.tiff;
|
||||
|
||||
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* YCbCr16UpsamplerStreamTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: YCbCr16UpsamplerStreamTest.java,v 1.0 31.01.13 14:35 haraldk Exp$
|
||||
*/
|
||||
public class YCbCr16UpsamplerStreamTest {
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateNullStream() {
|
||||
new YCbCr16UpsamplerStream(null, new int[2], 7, 5, null, ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateNullChroma() {
|
||||
new YCbCr16UpsamplerStream(new ByteArrayInputStream(new byte[0]), new int[3], 7, 5, null, ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateShortChroma() {
|
||||
new YCbCr16UpsamplerStream(new ByteArrayInputStream(new byte[0]), new int[1], 7, 5, null, ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateNoByteOrder() {
|
||||
new YCbCr16UpsamplerStream(new ByteArrayInputStream(new byte[0]), new int[] {2, 2}, 7, 5, null, null);
|
||||
}
|
||||
|
||||
// TODO: The expected values seems bogus...
|
||||
// But visually, it looks okay for the one and only sample image I've got...
|
||||
|
||||
@Test
|
||||
public void testUpsample22() throws IOException {
|
||||
short[] shorts = new short[] {
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 42, 96,
|
||||
108, 109, 110, 111, 112, 113, 114, 115, 43, 97
|
||||
};
|
||||
|
||||
byte[] bytes = new byte[shorts.length * 2];
|
||||
ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(shorts);
|
||||
|
||||
InputStream stream = new YCbCr16UpsamplerStream(new ByteArrayInputStream(bytes), new int[] {2, 2}, TIFFExtension.YCBCR_POSITIONING_CENTERED, 8, null, ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
short[] expected = new short[] {
|
||||
0, -30864, 0, 0, -30863, 0, 0, -30966, 0, 0, -30965, 0, 0, -30870, 0, 0, -30869, 0, 0, -30815, 0, 0, -30761, 0,
|
||||
0, -30862, 0, 0, -30861, 0, 0, -30931, 0, 0, -30877, 0, 0, -30868, 0, 0, -30867, 0, 0, -30858, 0, 0, -30858, 0
|
||||
};
|
||||
short[] upsampled = new short[expected.length];
|
||||
|
||||
LittleEndianDataInputStream dataInput = new LittleEndianDataInputStream(stream);
|
||||
for (int i = 0; i < upsampled.length; i++) {
|
||||
upsampled[i] = dataInput.readShort();
|
||||
}
|
||||
|
||||
assertArrayEquals(expected, upsampled);
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpsample21() throws IOException {
|
||||
short[] shorts = new short[] {
|
||||
1, 2, 3, 4, 42, 96, 77,
|
||||
112, 113, 114, 115, 43, 97, 43
|
||||
};
|
||||
|
||||
byte[] bytes = new byte[shorts.length * 2];
|
||||
ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).asShortBuffer().put(shorts);
|
||||
InputStream stream = new YCbCr16UpsamplerStream(new ByteArrayInputStream(bytes), new int[] {2, 1}, TIFFExtension.YCBCR_POSITIONING_CENTERED, 4, null, ByteOrder.BIG_ENDIAN);
|
||||
|
||||
short[] expected = new short[] {
|
||||
0, -30861, 0, 0, -30860, 0, 0, -30923, 0, 0, -30869, 0, 0, -30816, 0, 0, -30815, 0, 0, -30868, 0, 0, -30922, 0
|
||||
};
|
||||
short[] upsampled = new short[expected.length];
|
||||
|
||||
DataInputStream dataInput = new DataInputStream(stream);
|
||||
for (int i = 0; i < upsampled.length; i++) {
|
||||
upsampled[i] = dataInput.readShort();
|
||||
}
|
||||
|
||||
assertArrayEquals(expected, upsampled);
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpsample12() throws IOException {
|
||||
short[] shorts = new short[] {
|
||||
1, 2, 3, 4, 42, 96, 77,
|
||||
112, 113, 114, 115, 43, 97, 43
|
||||
};
|
||||
|
||||
byte[] bytes = new byte[shorts.length * 2];
|
||||
ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).asShortBuffer().put(shorts);
|
||||
InputStream stream = new YCbCr16UpsamplerStream(new ByteArrayInputStream(bytes), new int[] {1, 2}, TIFFExtension.YCBCR_POSITIONING_CENTERED, 4, null, ByteOrder.BIG_ENDIAN);
|
||||
|
||||
short[] expected = new short[] {
|
||||
0, -30861, 0, 0, -30923, 0, 0, -30816, 0, 0, -30761, 0, 0, -30860, 0, 0, -30869, 0, 0, -30815, 0, 0, -30815, 0
|
||||
};
|
||||
short[] upsampled = new short[expected.length];
|
||||
|
||||
DataInputStream dataInput = new DataInputStream(stream);
|
||||
for (int i = 0; i < upsampled.length; i++) {
|
||||
upsampled[i] = dataInput.readShort();
|
||||
}
|
||||
|
||||
assertArrayEquals(expected, upsampled);
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -33,6 +33,7 @@ import org.junit.Test;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@@ -65,7 +66,7 @@ public class YCbCrUpsamplerStreamTest {
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 42, 96,
|
||||
108, 109, 110, 111, 112, 113, 114, 115, 43, 97
|
||||
};
|
||||
YCbCrUpsamplerStream stream = new YCbCrUpsamplerStream(new ByteArrayInputStream(bytes), new int[] {2, 2}, TIFFExtension.YCBCR_POSITIONING_CENTERED, 8, null);
|
||||
InputStream stream = new YCbCrUpsamplerStream(new ByteArrayInputStream(bytes), new int[] {2, 2}, TIFFExtension.YCBCR_POSITIONING_CENTERED, 8, null);
|
||||
|
||||
byte[] expected = new byte[] {
|
||||
0, -126, 0, 0, -125, 0, 0, 27, 0, 0, 28, 0, 92, 124, 85, 93, 125, 86, 0, -78, 0, 0, -24, 0,
|
||||
@@ -85,7 +86,7 @@ public class YCbCrUpsamplerStreamTest {
|
||||
1, 2, 3, 4, 42, 96, 77,
|
||||
112, 113, 114, 115, 43, 97, 43
|
||||
};
|
||||
YCbCrUpsamplerStream stream = new YCbCrUpsamplerStream(new ByteArrayInputStream(bytes), new int[] {2, 1}, TIFFExtension.YCBCR_POSITIONING_CENTERED, 4, null);
|
||||
InputStream stream = new YCbCrUpsamplerStream(new ByteArrayInputStream(bytes), new int[] {2, 1}, TIFFExtension.YCBCR_POSITIONING_CENTERED, 4, null);
|
||||
|
||||
byte[] expected = new byte[] {
|
||||
0, -123, 0, 0, -122, 0, 20, 71, 0, 74, 125, 6, 0, -78, 90, 0, -77, 91, 75, 126, 7, 21, 72, 0
|
||||
@@ -104,7 +105,7 @@ public class YCbCrUpsamplerStreamTest {
|
||||
1, 2, 3, 4, 42, 96, 77,
|
||||
112, 113, 114, 115, 43, 97, 43
|
||||
};
|
||||
YCbCrUpsamplerStream stream = new YCbCrUpsamplerStream(new ByteArrayInputStream(bytes), new int[] {1, 2}, TIFFExtension.YCBCR_POSITIONING_CENTERED, 4, null);
|
||||
InputStream stream = new YCbCrUpsamplerStream(new ByteArrayInputStream(bytes), new int[] {1, 2}, TIFFExtension.YCBCR_POSITIONING_CENTERED, 4, null);
|
||||
|
||||
byte[] expected = new byte[] {
|
||||
0, -123, 0, 20, 71, 0, 0, -78, 90, 0, -24, 0, 0, -122, 0, 74, 125, 6, 0, -77, 91, 0, -78, 0
|
||||
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user