mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-24 00:00:01 -04:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c278f5f1f2 | |||
| 4c276df63d | |||
| 98616e9c9e | |||
| d8dedc86fc | |||
| fe9f345d7b | |||
| 31c3fdc0fd | |||
| 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:
|
* Read support for the following file types:
|
||||||
* Monochrome, 1 channel, 1 bit
|
* Monochrome, 1 channel, 1 bit
|
||||||
* Indexed, 1 channel, 8 bit
|
* Indexed, 1 channel, 8 bit
|
||||||
* Gray, 1 channel, 8 and 16 bit
|
* Gray, 1 channel, 8, 16 and 32 bit
|
||||||
* Duotone, 1 channel, 8 and 16 bit
|
* Duotone, 1 channel, 8, 16 and 32 bit
|
||||||
* RGB, 3-4 channels, 8 and 16 bit
|
* RGB, 3-4 channels, 8, 16 and 32 bit
|
||||||
* CMYK, 4-5 channels, 8 and 16 bit
|
* CMYK, 4-5 channels, 8, 16 and 32 bit
|
||||||
* Read support for the following compression types:
|
* Read support for the following compression types:
|
||||||
* Uncompressed
|
* Uncompressed
|
||||||
* RLE (PackBits)
|
* 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
|
* Thumbnail support
|
||||||
* JPEG
|
* JPEG
|
||||||
* RAW (RGB)
|
* RAW (RGB)
|
||||||
|
* Support for "Large Document Format" (PSB)
|
||||||
|
|
||||||
#### Aldus/Adobe Tagged Image File Format (TIFF)
|
#### Aldus/Adobe Tagged Image File Format (TIFF)
|
||||||
|
|
||||||
@@ -343,6 +344,10 @@ Build the project (using [Maven](http://maven.apache.org/download.cgi)):
|
|||||||
|
|
||||||
$ mvn package
|
$ 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`
|
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`.
|
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>
|
<dependency>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio-jpeg</artifactId>
|
<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>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio-tiff</artifactId>
|
<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>
|
</dependency>
|
||||||
</dependencies>
|
</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:
|
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-lang-3.0-rc5.jar
|
||||||
twelvemonkeys-common-io-3.0-SNAPSHOT.jar
|
twelvemonkeys-common-io-3.0-rc5.jar
|
||||||
twelvemonkeys-common-image-3.0-SNAPSHOT.jar
|
twelvemonkeys-common-image-3.0-rc5.jar
|
||||||
twelvemonkeys-imageio-core-3.0-SNAPSHOT.jar
|
twelvemonkeys-imageio-core-3.0-rc5.jar
|
||||||
twelvemonkeys-imageio-metadata-3.0-SNAPSHOT.jar
|
twelvemonkeys-imageio-metadata-3.0-rc5.jar
|
||||||
twelvemonkeys-imageio-jpeg-3.0-SNAPSHOT.jar
|
twelvemonkeys-imageio-jpeg-3.0-rc5.jar
|
||||||
twelvemonkeys-imageio-tiff-3.0-SNAPSHOT.jar
|
twelvemonkeys-imageio-tiff-3.0-rc5.jar
|
||||||
|
|
||||||
### Links to prebuilt binaries
|
### 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
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-image</artifactId>
|
<artifactId>common-image</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|||||||
@@ -1,27 +1,18 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import org.junit.Test;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.WindowAdapter;
|
|
||||||
import java.awt.event.WindowEvent;
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.IndexColorModel;
|
import java.awt.image.IndexColorModel;
|
||||||
import java.awt.image.RenderedImage;
|
import java.awt.image.RenderedImage;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
|
|
||||||
/**
|
import static org.junit.Assert.*;
|
||||||
* Created by IntelliJ IDEA.
|
|
||||||
*
|
public class ImageUtilTestCase {
|
||||||
* @author $author wmhakur$
|
|
||||||
* @version $id: $
|
|
||||||
* To change this template use Options | File Templates.
|
|
||||||
*/
|
|
||||||
public class ImageUtilTestCase extends TestCase {
|
|
||||||
|
|
||||||
private final static String IMAGE_NAME = "/sunflower.jpg";
|
private final static String IMAGE_NAME = "/sunflower.jpg";
|
||||||
private BufferedImage original;
|
private BufferedImage original;
|
||||||
@@ -56,6 +47,7 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testToBufferedImageNull() {
|
public void testToBufferedImageNull() {
|
||||||
BufferedImage img = null;
|
BufferedImage img = null;
|
||||||
boolean threwRuntimeException = false;
|
boolean threwRuntimeException = false;
|
||||||
@@ -73,6 +65,7 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
assertTrue(threwRuntimeException);
|
assertTrue(threwRuntimeException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testToBufferedImageTypeNull() {
|
public void testToBufferedImageTypeNull() {
|
||||||
BufferedImage img = null;
|
BufferedImage img = null;
|
||||||
boolean threwRuntimeException = false;
|
boolean threwRuntimeException = false;
|
||||||
@@ -90,6 +83,7 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
assertTrue(threwRuntimeException);
|
assertTrue(threwRuntimeException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testImageIsNotBufferedImage() {
|
public void testImageIsNotBufferedImage() {
|
||||||
// Should not be a buffered image
|
// Should not be a buffered image
|
||||||
assertFalse(
|
assertFalse(
|
||||||
@@ -98,6 +92,7 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testToBufferedImage() {
|
public void testToBufferedImage() {
|
||||||
BufferedImage sameAsImage = ImageUtil.toBuffered((RenderedImage) image);
|
BufferedImage sameAsImage = ImageUtil.toBuffered((RenderedImage) image);
|
||||||
BufferedImage bufferedScaled = ImageUtil.toBuffered(scaled);
|
BufferedImage bufferedScaled = ImageUtil.toBuffered(scaled);
|
||||||
@@ -111,10 +106,11 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
|
|
||||||
// Hmmm...
|
// Hmmm...
|
||||||
assertTrue(new Integer(42).equals(bufferedScaled.getProperty("lucky-number"))
|
assertTrue(new Integer(42).equals(bufferedScaled.getProperty("lucky-number"))
|
||||||
|| bufferedScaled.getPropertyNames() == null
|
|| bufferedScaled.getPropertyNames() == null
|
||||||
|| bufferedScaled.getPropertyNames().length == 0);
|
|| bufferedScaled.getPropertyNames().length == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testToBufferedImageType() {
|
public void testToBufferedImageType() {
|
||||||
// Assumes image is TYPE_INT_ARGB
|
// Assumes image is TYPE_INT_ARGB
|
||||||
BufferedImage converted = ImageUtil.toBuffered(image, BufferedImage.TYPE_BYTE_INDEXED);
|
BufferedImage converted = ImageUtil.toBuffered(image, BufferedImage.TYPE_BYTE_INDEXED);
|
||||||
@@ -136,6 +132,7 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
assertEquals(image.getHeight(), convertedToo.getHeight());
|
assertEquals(image.getHeight(), convertedToo.getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testBrightness() {
|
public void testBrightness() {
|
||||||
final BufferedImage original = this.original;
|
final BufferedImage original = this.original;
|
||||||
assertNotNull(original);
|
assertNotNull(original);
|
||||||
@@ -181,7 +178,7 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
final BufferedImage brightenedMaxNegative = ImageUtil.toBuffered(ImageUtil.brightness(original, -2f));
|
final BufferedImage brightenedMaxNegative = ImageUtil.toBuffered(ImageUtil.brightness(original, -2f));
|
||||||
for (int y = 0; y < brightenedMaxNegative.getHeight(); y++) {
|
for (int y = 0; y < brightenedMaxNegative.getHeight(); y++) {
|
||||||
for (int x = 0; x < brightenedMaxNegative.getWidth(); x++) {
|
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() {
|
public void testContrast() {
|
||||||
final BufferedImage original = this.original;
|
final BufferedImage original = this.original;
|
||||||
|
|
||||||
@@ -273,7 +270,6 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
else {
|
else {
|
||||||
assertTrue("Contrast should be increased or same", oB <= cB && cB <= dB);
|
assertTrue("Contrast should be increased or same", oB <= cB && cB <= dB);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Assumed: Only primary colors (w/b/r/g/b/c/y/m)
|
// 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 r = rgb >> 16 & 0xFF;
|
||||||
int g = rgb >> 8 & 0xFF;
|
int g = rgb >> 8 & 0xFF;
|
||||||
int b = rgb & 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() {
|
public void testSharpen() {
|
||||||
final BufferedImage original = this.original;
|
final BufferedImage original = this.original;
|
||||||
|
|
||||||
@@ -390,10 +387,10 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
final BufferedImage sharpenedDefault = ImageUtil.sharpen(original, 0.3f);
|
final BufferedImage sharpenedDefault = ImageUtil.sharpen(original, 0.3f);
|
||||||
final BufferedImage sharpenedMore = ImageUtil.sharpen(original, 1.3f);
|
final BufferedImage sharpenedMore = ImageUtil.sharpen(original, 1.3f);
|
||||||
|
|
||||||
long diffOriginal = 0;
|
// long diffOriginal = 0;
|
||||||
long diffSharpened = 0;
|
// long diffSharpened = 0;
|
||||||
long diffDefault = 0;
|
// long diffDefault = 0;
|
||||||
long diffMore = 0;
|
// long diffMore = 0;
|
||||||
|
|
||||||
long absDiffOriginal = 0;
|
long absDiffOriginal = 0;
|
||||||
long absDiffSharpened = 0;
|
long absDiffSharpened = 0;
|
||||||
@@ -412,10 +409,10 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
int pdRGB = 0x00FFFFFF & sharpenedDefault.getRGB(x - 1, y);
|
int pdRGB = 0x00FFFFFF & sharpenedDefault.getRGB(x - 1, y);
|
||||||
int pmRGB = 0x00FFFFFF & sharpenedMore.getRGB(x - 1, y);
|
int pmRGB = 0x00FFFFFF & sharpenedMore.getRGB(x - 1, y);
|
||||||
|
|
||||||
diffOriginal += poRGB - oRGB;
|
// diffOriginal += poRGB - oRGB;
|
||||||
diffSharpened += psRGB - sRGB;
|
// diffSharpened += psRGB - sRGB;
|
||||||
diffDefault += pdRGB - dRGB;
|
// diffDefault += pdRGB - dRGB;
|
||||||
diffMore += pmRGB - mRGB;
|
// diffMore += pmRGB - mRGB;
|
||||||
|
|
||||||
absDiffOriginal += Math.abs(poRGB - oRGB);
|
absDiffOriginal += Math.abs(poRGB - oRGB);
|
||||||
absDiffSharpened += Math.abs(psRGB - sRGB);
|
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);
|
// assertEquals("Difference should not change", diffOriginal, diffSharpened);
|
||||||
assertTrue("Abs difference should increase", absDiffOriginal < absDiffSharpened);
|
assertTrue("Abs difference should increase", absDiffOriginal < absDiffSharpened);
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
||||||
@@ -438,62 +431,7 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
assertTrue("Abs difference should increase", absDiffSharpened < absDiffMore);
|
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) {
|
@Test
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testBlur() {
|
public void testBlur() {
|
||||||
final BufferedImage original = this.original;
|
final BufferedImage original = this.original;
|
||||||
|
|
||||||
@@ -515,17 +453,16 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
final BufferedImage blurredDefault = ImageUtil.blur(original, 1.5f);
|
final BufferedImage blurredDefault = ImageUtil.blur(original, 1.5f);
|
||||||
final BufferedImage blurredMore = ImageUtil.blur(original, 3f);
|
final BufferedImage blurredMore = ImageUtil.blur(original, 3f);
|
||||||
|
|
||||||
long diffOriginal = 0;
|
// long diffOriginal = 0;
|
||||||
long diffBlurred = 0;
|
// long diffBlurred = 0;
|
||||||
long diffDefault = 0;
|
// long diffDefault = 0;
|
||||||
long diffMore = 0;
|
// long diffMore = 0;
|
||||||
|
|
||||||
long absDiffOriginal = 0;
|
long absDiffOriginal = 0;
|
||||||
long absDiffBlurred = 0;
|
long absDiffBlurred = 0;
|
||||||
long absDiffDefault = 0;
|
long absDiffDefault = 0;
|
||||||
long absDiffMore = 0;
|
long absDiffMore = 0;
|
||||||
|
|
||||||
|
|
||||||
for (int y = 0; y < original.getHeight(); y++) {
|
for (int y = 0; y < original.getHeight(); y++) {
|
||||||
for (int x = 1; x < original.getWidth(); x++) {
|
for (int x = 1; x < original.getWidth(); x++) {
|
||||||
int oRGB = 0x00FFFFFF & original.getRGB(x, y);
|
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 pdRGB = 0x00FFFFFF & blurredDefault.getRGB(x - 1, y);
|
||||||
int pmRGB = 0x00FFFFFF & blurredMore.getRGB(x - 1, y);
|
int pmRGB = 0x00FFFFFF & blurredMore.getRGB(x - 1, y);
|
||||||
|
|
||||||
diffOriginal += poRGB - oRGB;
|
// diffOriginal += poRGB - oRGB;
|
||||||
diffBlurred += pbRGB - bRGB;
|
// diffBlurred += pbRGB - bRGB;
|
||||||
diffDefault += pdRGB - dRGB;
|
// diffDefault += pdRGB - dRGB;
|
||||||
diffMore += pmRGB - mRGB;
|
// diffMore += pmRGB - mRGB;
|
||||||
|
|
||||||
absDiffOriginal += Math.abs(poRGB - oRGB);
|
absDiffOriginal += Math.abs(poRGB - oRGB);
|
||||||
absDiffBlurred += Math.abs(pbRGB - bRGB);
|
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);
|
// assertEquals("Difference should not change", diffOriginal, diffBlurred);
|
||||||
assertTrue(String.format("Abs difference should decrease: %s <= %s", absDiffOriginal, absDiffBlurred), absDiffOriginal > absDiffBlurred);
|
assertTrue(String.format("Abs difference should decrease: %s <= %s", absDiffOriginal, absDiffBlurred), absDiffOriginal > absDiffBlurred);
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
||||||
@@ -562,6 +497,7 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
assertTrue("Abs difference should decrease", absDiffBlurred > absDiffMore);
|
assertTrue("Abs difference should decrease", absDiffBlurred > absDiffMore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testIndexImage() {
|
public void testIndexImage() {
|
||||||
BufferedImage sunflower = original;
|
BufferedImage sunflower = original;
|
||||||
|
|
||||||
@@ -571,4 +507,4 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
assertNotNull("Image was null", image);
|
assertNotNull("Image was null", image);
|
||||||
assertTrue(image.getColorModel() instanceof IndexColorModel);
|
assertTrue(image.getColorModel() instanceof IndexColorModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
@@ -9,6 +10,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ResampleOpTestCase
|
* ResampleOpTestCase
|
||||||
*
|
*
|
||||||
@@ -16,7 +19,7 @@ import java.util.List;
|
|||||||
* @author last modified by $Author: haku $
|
* @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 $
|
* @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) {
|
protected BufferedImage createImage(final int pWidth, final int pHeigth) {
|
||||||
return createImage(pWidth, pHeigth, BufferedImage.TYPE_INT_ARGB);
|
return createImage(pWidth, pHeigth, BufferedImage.TYPE_INT_ARGB);
|
||||||
@@ -36,6 +39,7 @@ public class ResampleOpTestCase extends TestCase {
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCreateImage() {
|
public void testCreateImage() {
|
||||||
// Sanity test the create method
|
// Sanity test the create method
|
||||||
BufferedImage image = createImage(79, 84);
|
BufferedImage image = createImage(79, 84);
|
||||||
@@ -94,170 +98,225 @@ public class ResampleOpTestCase extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1x1
|
// 1x1
|
||||||
|
@Test
|
||||||
public void testResample1x1Point() {
|
public void testResample1x1Point() {
|
||||||
assertResample(createImage(1, 1), 10, 11, ResampleOp.FILTER_POINT);
|
assertResample(createImage(1, 1), 10, 11, ResampleOp.FILTER_POINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample1x1Box() {
|
public void testResample1x1Box() {
|
||||||
assertResample(createImage(1, 1), 10, 11, ResampleOp.FILTER_BOX);
|
assertResample(createImage(1, 1), 10, 11, ResampleOp.FILTER_BOX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample1x1Triangle() {
|
public void testResample1x1Triangle() {
|
||||||
assertResample(createImage(1, 1), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
assertResample(createImage(1, 1), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample1x1Lanczos() {
|
public void testResample1x1Lanczos() {
|
||||||
assertResample(createImage(1, 1), 7, 49, ResampleOp.FILTER_LANCZOS);
|
assertResample(createImage(1, 1), 7, 49, ResampleOp.FILTER_LANCZOS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample1x1Gaussian() {
|
public void testResample1x1Gaussian() {
|
||||||
assertResample(createImage(1, 1), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
assertResample(createImage(1, 1), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample1x1Sinc() {
|
public void testResample1x1Sinc() {
|
||||||
assertResample(createImage(1, 1), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
assertResample(createImage(1, 1), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2x2
|
// 2x2
|
||||||
|
@Test
|
||||||
public void testResample2x2Point() {
|
public void testResample2x2Point() {
|
||||||
assertResample(createImage(2, 2), 10, 11, ResampleOp.FILTER_POINT);
|
assertResample(createImage(2, 2), 10, 11, ResampleOp.FILTER_POINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample2x2Box() {
|
public void testResample2x2Box() {
|
||||||
assertResample(createImage(2, 2), 10, 11, ResampleOp.FILTER_BOX);
|
assertResample(createImage(2, 2), 10, 11, ResampleOp.FILTER_BOX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample2x2Triangle() {
|
public void testResample2x2Triangle() {
|
||||||
assertResample(createImage(2, 2), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
assertResample(createImage(2, 2), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample2x2Lanczos() {
|
public void testResample2x2Lanczos() {
|
||||||
assertResample(createImage(2, 2), 7, 49, ResampleOp.FILTER_LANCZOS);
|
assertResample(createImage(2, 2), 7, 49, ResampleOp.FILTER_LANCZOS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample2x2Gaussian() {
|
public void testResample2x2Gaussian() {
|
||||||
assertResample(createImage(2, 2), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
assertResample(createImage(2, 2), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample2x2Sinc() {
|
public void testResample2x2Sinc() {
|
||||||
assertResample(createImage(2, 2), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
assertResample(createImage(2, 2), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3x3
|
// 3x3
|
||||||
|
@Test
|
||||||
public void testResample3x3Point() {
|
public void testResample3x3Point() {
|
||||||
assertResample(createImage(3, 3), 10, 11, ResampleOp.FILTER_POINT);
|
assertResample(createImage(3, 3), 10, 11, ResampleOp.FILTER_POINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample3x3Box() {
|
public void testResample3x3Box() {
|
||||||
assertResample(createImage(3, 3), 10, 11, ResampleOp.FILTER_BOX);
|
assertResample(createImage(3, 3), 10, 11, ResampleOp.FILTER_BOX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample3x3Triangle() {
|
public void testResample3x3Triangle() {
|
||||||
assertResample(createImage(3, 3), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
assertResample(createImage(3, 3), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample3x3Lanczos() {
|
public void testResample3x3Lanczos() {
|
||||||
assertResample(createImage(3, 3), 7, 49, ResampleOp.FILTER_LANCZOS);
|
assertResample(createImage(3, 3), 7, 49, ResampleOp.FILTER_LANCZOS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample3x3Gaussian() {
|
public void testResample3x3Gaussian() {
|
||||||
assertResample(createImage(3, 3), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
assertResample(createImage(3, 3), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample3x3Sinc() {
|
public void testResample3x3Sinc() {
|
||||||
assertResample(createImage(3, 3), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
assertResample(createImage(3, 3), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4x4
|
// 4x4
|
||||||
|
@Test
|
||||||
public void testResample4x4Point() {
|
public void testResample4x4Point() {
|
||||||
assertResample(createImage(4, 4), 10, 11, ResampleOp.FILTER_POINT);
|
assertResample(createImage(4, 4), 10, 11, ResampleOp.FILTER_POINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample4x4Box() {
|
public void testResample4x4Box() {
|
||||||
assertResample(createImage(4, 4), 10, 11, ResampleOp.FILTER_BOX);
|
assertResample(createImage(4, 4), 10, 11, ResampleOp.FILTER_BOX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample4x4Triangle() {
|
public void testResample4x4Triangle() {
|
||||||
assertResample(createImage(4, 4), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
assertResample(createImage(4, 4), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample4x4Lanczos() {
|
public void testResample4x4Lanczos() {
|
||||||
assertResample(createImage(4, 4), 7, 49, ResampleOp.FILTER_LANCZOS);
|
assertResample(createImage(4, 4), 7, 49, ResampleOp.FILTER_LANCZOS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample4x4Gaussian() {
|
public void testResample4x4Gaussian() {
|
||||||
assertResample(createImage(4, 4), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
assertResample(createImage(4, 4), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample4x4Sinc() {
|
public void testResample4x4Sinc() {
|
||||||
assertResample(createImage(4, 4), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
assertResample(createImage(4, 4), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 20x20
|
// 20x20
|
||||||
|
@Test
|
||||||
public void testResample20x20Point() {
|
public void testResample20x20Point() {
|
||||||
assertResample(createImage(20, 20), 10, 11, ResampleOp.FILTER_POINT);
|
assertResample(createImage(20, 20), 10, 11, ResampleOp.FILTER_POINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample20x20Box() {
|
public void testResample20x20Box() {
|
||||||
assertResample(createImage(20, 20), 10, 11, ResampleOp.FILTER_BOX);
|
assertResample(createImage(20, 20), 10, 11, ResampleOp.FILTER_BOX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample20x20Triangle() {
|
public void testResample20x20Triangle() {
|
||||||
assertResample(createImage(20, 20), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
assertResample(createImage(20, 20), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample20x20Lanczos() {
|
public void testResample20x20Lanczos() {
|
||||||
assertResample(createImage(20, 20), 7, 49, ResampleOp.FILTER_LANCZOS);
|
assertResample(createImage(20, 20), 7, 49, ResampleOp.FILTER_LANCZOS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample20x20Gaussian() {
|
public void testResample20x20Gaussian() {
|
||||||
assertResample(createImage(20, 20), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
assertResample(createImage(20, 20), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample20x20Sinc() {
|
public void testResample20x20Sinc() {
|
||||||
assertResample(createImage(20, 20), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
assertResample(createImage(20, 20), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 200x160
|
// 200x160
|
||||||
|
@Test
|
||||||
public void testResample200x160Point() {
|
public void testResample200x160Point() {
|
||||||
assertResample(createImage(200, 160), 10, 11, ResampleOp.FILTER_POINT);
|
assertResample(createImage(200, 160), 10, 11, ResampleOp.FILTER_POINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample200x160Box() {
|
public void testResample200x160Box() {
|
||||||
assertResample(createImage(200, 160), 10, 11, ResampleOp.FILTER_BOX);
|
assertResample(createImage(200, 160), 10, 11, ResampleOp.FILTER_BOX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample200x160Triangle() {
|
public void testResample200x160Triangle() {
|
||||||
assertResample(createImage(200, 160), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
assertResample(createImage(200, 160), 19, 13, ResampleOp.FILTER_TRIANGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample200x160Lanczos() {
|
public void testResample200x160Lanczos() {
|
||||||
assertResample(createImage(200, 160), 7, 49, ResampleOp.FILTER_LANCZOS);
|
assertResample(createImage(200, 160), 7, 49, ResampleOp.FILTER_LANCZOS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample200x160Gaussian() {
|
public void testResample200x160Gaussian() {
|
||||||
assertResample(createImage(200, 160), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
assertResample(createImage(200, 160), 11, 34, ResampleOp.FILTER_GAUSSIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResample200x160Sinc() {
|
public void testResample200x160Sinc() {
|
||||||
assertResample(createImage(200, 160), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
assertResample(createImage(200, 160), 2, 8, ResampleOp.FILTER_BLACKMAN_SINC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test 10x10 -> 15x5 with different algorithms and types
|
// Test 10x10 -> 15x5 with different algorithms and types
|
||||||
|
@Test
|
||||||
public void testResamplePoint() {
|
public void testResamplePoint() {
|
||||||
assertResampleBufferedImageTypes(ResampleOp.FILTER_POINT);
|
assertResampleBufferedImageTypes(ResampleOp.FILTER_POINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResampleBox() {
|
public void testResampleBox() {
|
||||||
assertResampleBufferedImageTypes(ResampleOp.FILTER_BOX);
|
assertResampleBufferedImageTypes(ResampleOp.FILTER_BOX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResampleTriangle() {
|
public void testResampleTriangle() {
|
||||||
assertResampleBufferedImageTypes(ResampleOp.FILTER_TRIANGLE);
|
assertResampleBufferedImageTypes(ResampleOp.FILTER_TRIANGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResampleLanczos() {
|
public void testResampleLanczos() {
|
||||||
assertResampleBufferedImageTypes(ResampleOp.FILTER_LANCZOS);
|
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>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-io</artifactId>
|
<artifactId>common-io</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|||||||
@@ -57,9 +57,10 @@ public interface Decoder {
|
|||||||
* @return the total number of bytes read into the buffer, or {@code 0}
|
* @return the total number of bytes read into the buffer, or {@code 0}
|
||||||
* if there is no more data because the end of the stream has been reached.
|
* if there is no more data because the end of the stream has been reached.
|
||||||
*
|
*
|
||||||
* @throws DecodeException if encoded data is corrupt
|
* @throws DecodeException if encoded data is corrupt.
|
||||||
* @throws IOException if an I/O error occurs
|
* @throws IOException if an I/O error occurs.
|
||||||
* @throws java.io.EOFException if a premature end-of-file is encountered
|
* @throws java.io.EOFException if a premature end-of-file is encountered.
|
||||||
|
* @throws java.lang.NullPointerException if either argument is {@code null}.
|
||||||
*/
|
*/
|
||||||
int decode(InputStream stream, ByteBuffer buffer) throws IOException;
|
int decode(InputStream stream, ByteBuffer buffer) throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,9 +156,9 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Skipped can never be more than avail, which is
|
// NOTE: Skipped can never be more than avail, which is an int, so the cast is safe
|
||||||
// an int, so the cast is safe
|
|
||||||
int skipped = (int) Math.min(pLength - total, buffer.remaining());
|
int skipped = (int) Math.min(pLength - total, buffer.remaining());
|
||||||
|
buffer.position(buffer.position() + skipped);
|
||||||
total += skipped;
|
total += skipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-lang</artifactId>
|
<artifactId>common-lang</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys</groupId>
|
<groupId>com.twelvemonkeys</groupId>
|
||||||
<artifactId>twelvemonkeys</artifactId>
|
<artifactId>twelvemonkeys</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-batik</artifactId>
|
<artifactId>imageio-batik</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
||||||
|
|||||||
+11
@@ -29,10 +29,13 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.svg;
|
package com.twelvemonkeys.imageio.plugins.svg;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.ImagingOpException;
|
import java.awt.image.ImagingOpException;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -77,6 +80,7 @@ public class SVGImageReaderTestCase extends ImageReaderAbstractTestCase<SVGImage
|
|||||||
return Arrays.asList("image/svg+xml");
|
return Arrays.asList("image/svg+xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
@Override
|
@Override
|
||||||
public void testReadWithSizeParam() {
|
public void testReadWithSizeParam() {
|
||||||
try {
|
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;
|
package com.twelvemonkeys.imageio.plugins.wmf;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -76,4 +79,11 @@ public class WMFImageReaderTestCase extends ImageReaderAbstractTestCase<WMFImage
|
|||||||
protected List<String> getMIMETypes() {
|
protected List<String> getMIMETypes() {
|
||||||
return Arrays.asList("image/x-wmf", "application/x-msmetafile");
|
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>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-core</artifactId>
|
<artifactId>imageio-core</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ public final class ColorSpaces {
|
|||||||
* to avoid unnecessary array copying.
|
* to avoid unnecessary array copying.
|
||||||
*/
|
*/
|
||||||
private static boolean fixProfileXYZTag(ICC_Profile profile, final int tagSignature) {
|
private static boolean fixProfileXYZTag(ICC_Profile profile, final int tagSignature) {
|
||||||
|
// TODO: This blows up on OpenJDK... Bug?
|
||||||
byte[] data = profile.getData(tagSignature);
|
byte[] data = profile.getData(tagSignature);
|
||||||
|
|
||||||
// The CMM expects 0x64 65 73 63 ('XYZ ') but is 0x17 A5 05 B8..?
|
// 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$
|
* @version $Id: ImageReaderAbstractTestCase.java,v 1.0 Apr 1, 2008 10:36:46 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
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
|
// - Pro: Tests the META-INF/services config
|
||||||
// - Con: Not all providers should be installed at runtime...
|
// - 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());
|
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
|
@Test
|
||||||
public void testReadWithSizeAndSourceRegionParam() {
|
public void testReadWithSizeAndSourceRegionParam() {
|
||||||
// TODO: Is this test correct???
|
// TODO: Is this test correct???
|
||||||
@@ -597,10 +635,8 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
|||||||
failBecause("Image could not be read", e);
|
failBecause("Image could not be read", e);
|
||||||
}
|
}
|
||||||
assertNotNull("Image was null!", image);
|
assertNotNull("Image was null!", image);
|
||||||
assertEquals("Read image has wrong width: " + image.getWidth(),
|
assertEquals("Read image has wrong width: " + image.getWidth(), 5, image.getWidth());
|
||||||
5, image.getWidth());
|
assertEquals("Read image has wrong height: " + image.getHeight(), 5, image.getHeight());
|
||||||
assertEquals("Read image has wrong height: " + image.getHeight(),
|
|
||||||
5, image.getHeight());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-icns</artifactId>
|
<artifactId>imageio-icns</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
|
||||||
|
|||||||
+7
@@ -138,4 +138,11 @@ public class ICNSImageReaderTest extends ImageReaderAbstractTestCase {
|
|||||||
public void testReadWithSubsampleParamPixels() throws IOException {
|
public void testReadWithSubsampleParamPixels() throws IOException {
|
||||||
super.testReadWithSubsampleParamPixels();
|
super.testReadWithSubsampleParamPixels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore("Known issue: Source region reading not supported")
|
||||||
|
@Override
|
||||||
|
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||||
|
super.testReadWithSourceRegionParamEqualImage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-ico</artifactId>
|
<artifactId>imageio-ico</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: ICO plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: ICO plugin</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-iff</artifactId>
|
<artifactId>imageio-iff</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-jmagick</artifactId>
|
<artifactId>imageio-jmagick</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: JMagick Plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: JMagick Plugin</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-jpeg</artifactId>
|
<artifactId>imageio-jpeg</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
|
||||||
|
|||||||
+13
-8
@@ -212,7 +212,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
|||||||
@Override
|
@Override
|
||||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||||
Iterator<ImageTypeSpecifier> types = delegate.getImageTypes(imageIndex);
|
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) {
|
if (types == null || !types.hasNext() || csType == JPEGColorSpace.CMYK || csType == JPEGColorSpace.YCCK) {
|
||||||
ArrayList<ImageTypeSpecifier> typeList = new ArrayList<ImageTypeSpecifier>();
|
ArrayList<ImageTypeSpecifier> typeList = new ArrayList<ImageTypeSpecifier>();
|
||||||
@@ -266,7 +266,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, consult the image metadata
|
// Otherwise, consult the image metadata
|
||||||
JPEGColorSpace csType = getSourceCSType(getAdobeDCT(), getSOF());
|
JPEGColorSpace csType = getSourceCSType(getJFIF(), getAdobeDCT(), getSOF());
|
||||||
|
|
||||||
switch (csType) {
|
switch (csType) {
|
||||||
case CMYK:
|
case CMYK:
|
||||||
@@ -318,7 +318,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
|||||||
ICC_Profile profile = getEmbeddedICCProfile(false);
|
ICC_Profile profile = getEmbeddedICCProfile(false);
|
||||||
AdobeDCTSegment adobeDCT = getAdobeDCT();
|
AdobeDCTSegment adobeDCT = getAdobeDCT();
|
||||||
SOFSegment sof = getSOF();
|
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)
|
// 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.
|
// - 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
|
// 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
|
// that requires 2 x memory or more, so a few steps is an ok compromise I guess
|
||||||
try {
|
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;
|
final int srcMaxY = srcRegion.y + srcRegion.height;
|
||||||
int destY = dstRegion.y;
|
int destY = dstRegion.y;
|
||||||
|
|
||||||
@@ -448,6 +448,11 @@ public class JPEGImageReader extends ImageReaderBase {
|
|||||||
// Let the progress delegator handle progress, using corrected range
|
// Let the progress delegator handle progress, using corrected range
|
||||||
progressDelegator.updateProgressRange(100f * (y + scan) / srcRegion.height);
|
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);
|
Rectangle subRegion = new Rectangle(srcRegion.x, y, srcRegion.width, scan);
|
||||||
param.setSourceRegion(subRegion);
|
param.setSourceRegion(subRegion);
|
||||||
Raster raster = delegate.readRaster(imageIndex, param); // non-converted
|
Raster raster = delegate.readRaster(imageIndex, param); // non-converted
|
||||||
@@ -499,7 +504,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
|||||||
return image;
|
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:
|
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:
|
case 4:
|
||||||
if (startOfFrame.components[0].id == 1 && startOfFrame.components[1].id == 2 && startOfFrame.components[2].id == 3 && startOfFrame.components[3].id == 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)
|
// See ColorConvertOp#filter(Raster, WritableRaster)
|
||||||
|
|
||||||
if (profile != null && profile.getProfileClass() != ICC_Profile.CLASS_DISPLAY) {
|
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) {
|
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
|
intToBigEndian(ICC_Profile.icSigDisplayClass, profileData, ICC_Profile.icHdrDeviceClass); // Header is first
|
||||||
|
|
||||||
|
|||||||
+15
-10
@@ -89,20 +89,25 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
|
|||||||
// Scan forward
|
// Scan forward
|
||||||
while (true) {
|
while (true) {
|
||||||
long realPosition = stream.getStreamPosition();
|
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;
|
int trash = 0;
|
||||||
while (marker == 0) {
|
int marker = stream.readUnsignedByte();
|
||||||
marker = stream.readUnsignedShort();
|
|
||||||
trash += 2;
|
// Skip bad padding before the marker
|
||||||
|
while (marker != 0xff) {
|
||||||
|
marker = stream.readUnsignedByte();
|
||||||
|
trash++;
|
||||||
|
realPosition++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (marker == 0x00ff) {
|
if (trash != 0) {
|
||||||
trash++;
|
// NOTE: We previously allowed these bytes to pass through to the native reader, as it could cope
|
||||||
marker = 0xff00 | stream.readUnsignedByte();
|
// 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
|
// Skip over 0xff padding between markers
|
||||||
while (marker == 0xffff) {
|
while (marker == 0xffff) {
|
||||||
realPosition++;
|
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
|
// 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) {
|
if (isAppSegmentMarker(marker) && !(marker == JPEG.APP1 && isAppSegmentWithId("Exif", stream)) && marker != JPEG.APP14) {
|
||||||
int length = stream.readUnsignedShort(); // Length including length field itself
|
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 {
|
else {
|
||||||
if (marker == JPEG.EOI) {
|
if (marker == JPEG.EOI) {
|
||||||
@@ -129,7 +134,7 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Length including length field itself
|
// Length including length field itself
|
||||||
length = trash + stream.readUnsignedShort() + 2;
|
length = stream.readUnsignedShort() + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
segment = new Segment(marker, realPosition, segment.end(), length);
|
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 com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||||
import org.hamcrest.core.IsInstanceOf;
|
import org.hamcrest.core.IsInstanceOf;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.internal.matchers.GreaterThan;
|
import org.mockito.internal.matchers.GreaterThan;
|
||||||
import org.w3c.dom.Element;
|
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.jpg"), new Dimension(160, 227)),
|
||||||
new TestData(getClassLoaderResource("/jpeg/cmyk-sample-multiple-chunk-icc.jpg"), new Dimension(2707, 3804)),
|
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-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
|
// More test data in specific tests below
|
||||||
@@ -107,7 +109,6 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked"})
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<JPEGImageReader> getReaderClass() {
|
protected Class<JPEGImageReader> getReaderClass() {
|
||||||
return JPEGImageReader.class;
|
return JPEGImageReader.class;
|
||||||
@@ -329,6 +330,32 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
|||||||
verify(warningListener).warningOccurred(eq(reader), anyString());
|
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
|
@Test
|
||||||
public void testCorbisRGB() throws IOException {
|
public void testCorbisRGB() throws IOException {
|
||||||
// Special case, throws exception below without special treatment
|
// Special case, throws exception below without special treatment
|
||||||
@@ -486,9 +513,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
|||||||
// Validate strip colors
|
// Validate strip colors
|
||||||
for (int i = 0; i < strip.getWidth() / 128; i++) {
|
for (int i = 0; i < strip.getWidth() / 128; i++) {
|
||||||
int actualRGB = strip.getRGB(i * 128, 4);
|
int actualRGB = strip.getRGB(i * 128, 4);
|
||||||
assertEquals((actualRGB >> 16) & 0xff, (expectedRGB[i] >> 16) & 0xff, 5);
|
assertRGBEquals(expectedRGB[i], actualRGB);
|
||||||
assertEquals((actualRGB >> 8) & 0xff, (expectedRGB[i] >> 8) & 0xff, 5);
|
|
||||||
assertEquals((actualRGB) & 0xff, (expectedRGB[i]) & 0xff, 5);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,9 +541,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
|||||||
// Validate strip colors
|
// Validate strip colors
|
||||||
for (int i = 0; i < thumbnail.getWidth() / 8; i++) {
|
for (int i = 0; i < thumbnail.getWidth() / 8; i++) {
|
||||||
int actualRGB = thumbnail.getRGB(i * 8, 4);
|
int actualRGB = thumbnail.getRGB(i * 8, 4);
|
||||||
assertEquals((actualRGB >> 16) & 0xff, (expectedRGB[i] >> 16) & 0xff, 5);
|
assertRGBEquals(expectedRGB[i], actualRGB);
|
||||||
assertEquals((actualRGB >> 8) & 0xff, (expectedRGB[i] >> 8) & 0xff, 5);
|
|
||||||
assertEquals((actualRGB) & 0xff, (expectedRGB[i]) & 0xff, 5);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -653,12 +676,135 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
|||||||
// Validate strip colors
|
// Validate strip colors
|
||||||
for (int i = 0; i < image.getWidth() / 10; i++) {
|
for (int i = 0; i < image.getWidth() / 10; i++) {
|
||||||
int actualRGB = image.getRGB(i * 10, 7);
|
int actualRGB = image.getRGB(i * 10, 7);
|
||||||
assertEquals((actualRGB >> 16) & 0xff, (expectedRGB[i] >> 16) & 0xff, 5);
|
assertRGBEquals(expectedRGB[i], actualRGB);
|
||||||
assertEquals((actualRGB >> 8) & 0xff, (expectedRGB[i] >> 8) & 0xff, 5);
|
|
||||||
assertEquals((actualRGB ) & 0xff, (expectedRGB[i] ) & 0xff, 5);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
@Test
|
||||||
public void testXDensityOutOfRangeIssue() throws IOException {
|
public void testXDensityOutOfRangeIssue() throws IOException {
|
||||||
// Image has JFIF with x/y density 0
|
// Image has JFIF with x/y density 0
|
||||||
@@ -820,6 +966,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
|||||||
|
|
||||||
if (expectedTree == null) {
|
if (expectedTree == null) {
|
||||||
assertNull(actualTree);
|
assertNull(actualTree);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(String.format("%s: Node names differ", message), expectedTree.getNodeName(), actualTree.getNodeName());
|
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>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>imageio-metadata</artifactId>
|
<artifactId>imageio-metadata</artifactId>
|
||||||
|
|||||||
+10
@@ -120,6 +120,16 @@ final class EXIFEntry extends AbstractEntry {
|
|||||||
return "Artist";
|
return "Artist";
|
||||||
case TIFF.TAG_HOST_COMPUTER:
|
case TIFF.TAG_HOST_COMPUTER:
|
||||||
return "HostComputer";
|
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:
|
case TIFF.TAG_COPYRIGHT:
|
||||||
return "Copyright";
|
return "Copyright";
|
||||||
case TIFF.TAG_YCBCR_SUB_SAMPLING:
|
case TIFF.TAG_YCBCR_SUB_SAMPLING:
|
||||||
|
|||||||
+3
@@ -143,6 +143,9 @@ public interface TIFF {
|
|||||||
int TAG_WHITE_POINT = 318;
|
int TAG_WHITE_POINT = 318;
|
||||||
int TAG_PRIMARY_CHROMATICITIES = 319;
|
int TAG_PRIMARY_CHROMATICITIES = 319;
|
||||||
int TAG_COLOR_MAP = 320;
|
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_EXTRA_SAMPLES = 338;
|
||||||
int TAG_TRANSFER_RANGE = 342;
|
int TAG_TRANSFER_RANGE = 342;
|
||||||
int TAG_YCBCR_COEFFICIENTS = 529;
|
int TAG_YCBCR_COEFFICIENTS = 529;
|
||||||
|
|||||||
+20
-13
@@ -29,7 +29,9 @@
|
|||||||
package com.twelvemonkeys.imageio.metadata.jpeg;
|
package com.twelvemonkeys.imageio.metadata.jpeg;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||||
|
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||||
import com.twelvemonkeys.imageio.metadata.exif.EXIFReader;
|
import com.twelvemonkeys.imageio.metadata.exif.EXIFReader;
|
||||||
|
import com.twelvemonkeys.imageio.metadata.psd.PSD;
|
||||||
import com.twelvemonkeys.imageio.metadata.psd.PSDReader;
|
import com.twelvemonkeys.imageio.metadata.psd.PSDReader;
|
||||||
import com.twelvemonkeys.imageio.metadata.xmp.XMP;
|
import com.twelvemonkeys.imageio.metadata.xmp.XMP;
|
||||||
import com.twelvemonkeys.imageio.metadata.xmp.XMPReader;
|
import com.twelvemonkeys.imageio.metadata.xmp.XMPReader;
|
||||||
@@ -38,6 +40,8 @@ import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
|||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import java.awt.color.ICC_ColorSpace;
|
||||||
|
import java.awt.color.ICC_Profile;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.*;
|
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 {
|
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...?
|
// Skip trash padding before the marker
|
||||||
int bad = 0;
|
while (marker != 0xff) {
|
||||||
while (marker == 0) {
|
marker = stream.readUnsignedByte();
|
||||||
marker = stream.readUnsignedShort();
|
// trash++;
|
||||||
bad += 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (marker == 0x00ff) {
|
// if (trash != 0) {
|
||||||
bad++;
|
// TODO: Issue warning?
|
||||||
marker = 0xff00 | stream.readUnsignedByte();
|
// System.err.println("trash: " + trash);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (bad != 0) {
|
marker = 0xff00 | stream.readUnsignedByte();
|
||||||
// System.err.println("bad: " + bad);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip over 0xff padding between markers
|
// Skip over 0xff padding between markers
|
||||||
while (marker == 0xffff) {
|
while (marker == 0xffff) {
|
||||||
@@ -290,6 +292,11 @@ public final class JPEGSegmentUtil {
|
|||||||
// IPTC metadata. Probably duplicated in the XMP though...
|
// IPTC metadata. Probably duplicated in the XMP though...
|
||||||
ImageInputStream stream = new ByteArrayImageInputStream(segment.data, segment.offset(), segment.length());
|
ImageInputStream stream = new ByteArrayImageInputStream(segment.data, segment.offset(), segment.length());
|
||||||
Directory psd = new PSDReader().read(stream);
|
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("PSD: " + psd);
|
||||||
System.err.println(EXIFReader.HexDump.dump(segment.data));
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @version $Id: PSD.java,v 1.0 24.01.12 16:51 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public interface PSD {
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -136,6 +136,10 @@ public final class PSDReader extends MetadataReader {
|
|||||||
pInput.readFully(data);
|
pInput.readFully(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final int id() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
public final byte[] data() {
|
public final byte[] data() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-pdf</artifactId>
|
<artifactId>imageio-pdf</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-pict</artifactId>
|
<artifactId>imageio-pict</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
|
||||||
|
|||||||
+7
-2
@@ -2623,8 +2623,13 @@ public class PICTImageReader extends ImageReaderBase {
|
|||||||
Graphics2D g = image.createGraphics();
|
Graphics2D g = image.createGraphics();
|
||||||
try {
|
try {
|
||||||
// TODO: Might need to clear background
|
// TODO: Might need to clear background
|
||||||
|
AffineTransform instance = new AffineTransform();
|
||||||
g.setTransform(AffineTransform.getScaleInstance(screenImageXRatio / subX, screenImageYRatio / subY));
|
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 {
|
// try {
|
||||||
drawOnto(g);
|
drawOnto(g);
|
||||||
// }
|
// }
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-psd</artifactId>
|
<artifactId>imageio-psd</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: PSD plugin</name>
|
<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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2014, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: ICCProfile.java,v 1.0 May 20, 2008 6:24:10 PM haraldk Exp$
|
* @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;
|
private ICC_Profile profile;
|
||||||
|
|
||||||
ICCProfile(final short pId, final ImageInputStream pInput) throws IOException {
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSD.java,v 1.0 Apr 29, 2008 4:47:47 PM haraldk Exp$
|
* @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>
|
* @see <a href="http://www.fileformat.info/format/psd/egff.htm">http://www.fileformat.info/format/psd/egff.htm</a>
|
||||||
*/
|
*/
|
||||||
interface PSD {
|
interface PSD {
|
||||||
/** PSD 2+ Native format (.PSD) identifier "8BPS" */
|
/** PSD 2+ Native format (.PSD) identifier "8BPS" */
|
||||||
int SIGNATURE_8BPS = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'S';
|
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" */
|
/** PSD 5+ Large Document Format (.PSB) identifier "8BPB" */
|
||||||
int SIGNATURE_8BPB = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'B';
|
int SIGNATURE_8BPB = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'B';
|
||||||
|
|
||||||
|
int VERSION_PSD = 1;
|
||||||
|
int VERSION_PSB = 2;
|
||||||
|
|
||||||
/** PSD Resource type identifier "8BIM" */
|
/** PSD Resource type identifier "8BIM" */
|
||||||
int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M';
|
int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M';
|
||||||
|
|
||||||
@@ -105,7 +110,7 @@ interface PSD {
|
|||||||
|
|
||||||
// Color Modes
|
// Color Modes
|
||||||
/** Bitmap (monochrome) */
|
/** Bitmap (monochrome) */
|
||||||
short COLOR_MODE_MONOCHROME = 0;
|
short COLOR_MODE_BITMAP = 0;
|
||||||
|
|
||||||
/** Gray-scale */
|
/** Gray-scale */
|
||||||
short COLOR_MODE_GRAYSCALE = 1;
|
short COLOR_MODE_GRAYSCALE = 1;
|
||||||
@@ -541,4 +546,13 @@ interface PSD {
|
|||||||
*/
|
*/
|
||||||
int RES_PRINT_FLAGS_INFORMATION = 0x2710;
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDAlphaChannelInfo.java,v 1.0 May 2, 2008 5:33:40 PM haraldk Exp$
|
* @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;
|
List<String> names;
|
||||||
|
|
||||||
public PSDAlphaChannelInfo(short pId, final ImageInputStream pInput) throws IOException {
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDChannelInfo.java,v 1.0 May 6, 2008 2:46:23 PM haraldk Exp$
|
* @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 short channelId;
|
||||||
final long length;
|
final long length;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2014, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDChannelSourceDestinationRange.java,v 1.0 May 6, 2008 5:14:13 PM haraldk Exp$
|
* @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 String channel;
|
||||||
private short sourceBlack;
|
private short sourceBlack;
|
||||||
private short sourceWhite;
|
private short sourceWhite;
|
||||||
|
|||||||
Regular → Executable
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2014, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDColorData.java,v 1.0 Apr 29, 2008 5:33:01 PM haraldk Exp$
|
* @version $Id: PSDColorData.java,v 1.0 Apr 29, 2008 5:33:01 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
class PSDColorData {
|
final class PSDColorData {
|
||||||
final byte[] colors;
|
final byte[] colors;
|
||||||
private IndexColorModel colorModel;
|
private IndexColorModel colorModel;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2014, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$
|
* @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
|
// TODO: Size of this struct should be 14.. Does not compute... Something bogus here
|
||||||
|
|
||||||
// ColorSpace definitions:
|
// 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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDGlobalLayerMask.java,v 1.0 May 8, 2008 5:33:48 PM haraldk Exp$
|
* @version $Id: PSDGlobalLayerMask.java,v 1.0 May 8, 2008 5:33:48 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
class PSDGlobalLayerMask {
|
final class PSDGlobalLayerMask {
|
||||||
final int colorSpace;
|
final int colorSpace;
|
||||||
final int color1;
|
final int color1;
|
||||||
final int color2;
|
final int color2;
|
||||||
final int color3;
|
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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -28,8 +28,9 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.psd;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
|
||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
|
|
||||||
|
import java.io.DataInput;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,19 +40,21 @@ import java.io.IOException;
|
|||||||
* @author last modified by $Author: haraldk$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDHeader.java,v 1.0 Apr 29, 2008 5:18:22 PM haraldk Exp$
|
* @version $Id: PSDHeader.java,v 1.0 Apr 29, 2008 5:18:22 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
class PSDHeader {
|
final class PSDHeader {
|
||||||
// The header is 26 bytes in length and is structured as follows:
|
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
|
// typedef struct _PSD_HEADER
|
||||||
// {
|
// {
|
||||||
// BYTE Signature[4]; /* File ID "8BPS" */
|
// 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 */
|
// 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 */
|
// channels */
|
||||||
// LONG Rows; /* Height of image in pixels (1-30000) */
|
// LONG Rows; /* Height of image in pixels (1-30000/1-300000 for PSB) */
|
||||||
// LONG Columns; /* Width of image in pixels (1-30000) */
|
// LONG Columns; /* Width of image in pixels (1-30000/1-300000 for PSB) */
|
||||||
// WORD Depth; /* Number of bits per channel (1, 8, and 16) */
|
// WORD Depth; /* Number of bits per channel (1, 8, 16 or 32) */
|
||||||
// WORD Mode; /* Color mode */
|
// WORD Mode; /* Color mode */
|
||||||
// } PSD_HEADER;
|
// } PSD_HEADER;
|
||||||
|
|
||||||
@@ -60,8 +63,9 @@ class PSDHeader {
|
|||||||
final int height;
|
final int height;
|
||||||
final short bits;
|
final short bits;
|
||||||
final short mode;
|
final short mode;
|
||||||
|
final boolean largeFormat;
|
||||||
|
|
||||||
PSDHeader(final ImageInputStream pInput) throws IOException {
|
PSDHeader(final DataInput pInput) throws IOException {
|
||||||
int signature = pInput.readInt();
|
int signature = pInput.readInt();
|
||||||
if (signature != PSD.SIGNATURE_8BPS) {
|
if (signature != PSD.SIGNATURE_8BPS) {
|
||||||
throw new IIOException("Not a PSD document, expected signature \"8BPS\": \"" + PSDUtil.intToStr(signature) + "\" (0x" + Integer.toHexString(signature) + ")");
|
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();
|
int version = pInput.readUnsignedShort();
|
||||||
|
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case 1:
|
case PSD.VERSION_PSD:
|
||||||
|
largeFormat = false;
|
||||||
|
break;
|
||||||
|
case PSD.VERSION_PSB:
|
||||||
|
largeFormat = true;
|
||||||
break;
|
break;
|
||||||
case 2:
|
|
||||||
throw new IIOException("Photoshop Large Document Format (PSB) not supported yet.");
|
|
||||||
default:
|
default:
|
||||||
throw new IIOException(String.format("Unknown PSD version, expected 1 or 2: 0x%08x", version));
|
throw new IIOException(String.format("Unknown PSD version, expected 1 or 2: 0x%08x", version));
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] reserved = new byte[6];
|
byte[] reserved = new byte[6];
|
||||||
pInput.readFully(reserved);
|
pInput.readFully(reserved); // We don't really care
|
||||||
|
|
||||||
channels = pInput.readShort();
|
channels = pInput.readShort();
|
||||||
|
if (channels <= 0) {
|
||||||
|
throw new IIOException(String.format("Unsupported number of channels: %d", channels));
|
||||||
|
}
|
||||||
|
|
||||||
height = pInput.readInt(); // Rows
|
height = pInput.readInt(); // Rows
|
||||||
width = pInput.readInt(); // Columns
|
width = pInput.readInt(); // Columns
|
||||||
|
|
||||||
bits = pInput.readShort();
|
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();
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
|
return new StringBuilder(getClass().getSimpleName())
|
||||||
builder.append("[Channels: ");
|
.append("[version: ")
|
||||||
builder.append(channels);
|
.append(largeFormat ? "2" : "1")
|
||||||
builder.append(", width: ");
|
.append(", channels: ")
|
||||||
builder.append(width);
|
.append(channels)
|
||||||
builder.append(", height: ");
|
.append(", width: ")
|
||||||
builder.append(height);
|
.append(width)
|
||||||
builder.append(", depth: ");
|
.append(", height: ")
|
||||||
builder.append(bits);
|
.append(height)
|
||||||
builder.append(", mode: ");
|
.append(", depth: ")
|
||||||
builder.append(mode);
|
.append(bits)
|
||||||
switch (mode) {
|
.append(", mode: ")
|
||||||
case PSD.COLOR_MODE_MONOCHROME:
|
.append(mode)
|
||||||
builder.append(" (Monochrome)");
|
.append(" (")
|
||||||
break;
|
.append(modeAsString())
|
||||||
case PSD.COLOR_MODE_GRAYSCALE:
|
.append(")]")
|
||||||
builder.append(" (Grayscale)");
|
.toString();
|
||||||
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 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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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.ImageReaderBase;
|
||||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||||
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
|
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
|
||||||
import com.twelvemonkeys.xml.XMLSerializer;
|
|
||||||
import org.w3c.dom.Node;
|
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.ImageReadParam;
|
import javax.imageio.ImageReadParam;
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -70,12 +67,12 @@ import java.util.List;
|
|||||||
// See http://www.codeproject.com/KB/graphics/PSDParser.aspx
|
// See http://www.codeproject.com/KB/graphics/PSDParser.aspx
|
||||||
// See http://www.adobeforums.com/webx?14@@.3bc381dc/0
|
// See http://www.adobeforums.com/webx?14@@.3bc381dc/0
|
||||||
// Done: Allow reading the extra alpha channels (index after composite data)
|
// 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 PSDHeader header;
|
||||||
private ICC_ColorSpace colorSpace;
|
private ICC_ColorSpace colorSpace;
|
||||||
protected PSDMetadata metadata;
|
private PSDMetadata metadata;
|
||||||
|
|
||||||
protected PSDImageReader(final ImageReaderSpi originatingProvider) {
|
PSDImageReader(final ImageReaderSpi originatingProvider) {
|
||||||
super(originatingProvider);
|
super(originatingProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,10 +105,7 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int getLayerWidth(int layerIndex) throws IOException {
|
private int getLayerWidth(int layerIndex) throws IOException {
|
||||||
if (metadata == null || metadata.layerInfo == null) {
|
readLayerAndMaskInfo(true);
|
||||||
readImageResources(false);
|
|
||||||
readLayerAndMaskInfo(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex);
|
PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex);
|
||||||
|
|
||||||
@@ -119,10 +113,7 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int getLayerHeight(int layerIndex) throws IOException {
|
private int getLayerHeight(int layerIndex) throws IOException {
|
||||||
if (metadata == null || metadata.layerInfo == null) {
|
readLayerAndMaskInfo(true);
|
||||||
readImageResources(false);
|
|
||||||
readLayerAndMaskInfo(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex);
|
PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex);
|
||||||
|
|
||||||
@@ -136,14 +127,10 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
private ImageTypeSpecifier getRawImageTypeInternal(final int imageIndex) throws IOException {
|
private ImageTypeSpecifier getRawImageTypeInternal(final int imageIndex) throws IOException {
|
||||||
checkBounds(imageIndex);
|
checkBounds(imageIndex);
|
||||||
readHeader();
|
|
||||||
|
|
||||||
// Image index above 0, means a layer
|
// Image index above 0, means a layer
|
||||||
if (imageIndex > 0) {
|
if (imageIndex > 0) {
|
||||||
if (metadata == null || metadata.layerInfo == null) {
|
readLayerAndMaskInfo(true);
|
||||||
readImageResources(false);
|
|
||||||
readLayerAndMaskInfo(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return getRawImageTypeForLayer(imageIndex - 1);
|
return getRawImageTypeForLayer(imageIndex - 1);
|
||||||
}
|
}
|
||||||
@@ -154,46 +141,56 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
private ImageTypeSpecifier getRawImageTypeForCompositeLayer() throws IOException {
|
private ImageTypeSpecifier getRawImageTypeForCompositeLayer() throws IOException {
|
||||||
ColorSpace cs;
|
ColorSpace cs;
|
||||||
|
|
||||||
switch (header.mode) {
|
switch (header.mode) {
|
||||||
case PSD.COLOR_MODE_MONOCHROME:
|
case PSD.COLOR_MODE_BITMAP:
|
||||||
if (header.channels == 1 && header.bits == 1) {
|
if (header.channels == 1 && header.bits == 1) {
|
||||||
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY);
|
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IIOException(
|
throw new IIOException(String.format("Unsupported channel count/bit depth for Monochrome PSD: %d channels/%d bits", header.channels, header.bits));
|
||||||
String.format("Unsupported channel count/bit depth for Monochrome PSD: %d channels/%d bits", header.channels, header.bits)
|
|
||||||
);
|
|
||||||
|
|
||||||
case PSD.COLOR_MODE_INDEXED:
|
case PSD.COLOR_MODE_INDEXED:
|
||||||
// TODO: 16 bit indexed?! Does it exist?
|
|
||||||
if (header.channels == 1 && header.bits == 8) {
|
if (header.channels == 1 && header.bits == 8) {
|
||||||
return IndexedImageTypeSpecifier.createFromIndexColorModel(metadata.colorData.getIndexColorModel());
|
return IndexedImageTypeSpecifier.createFromIndexColorModel(metadata.colorData.getIndexColorModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IIOException(
|
throw new IIOException(String.format("Unsupported channel count/bit depth for Indexed Color PSD: %d channels/%d bits", header.channels, header.bits));
|
||||||
String.format("Unsupported channel count/bit depth for Indexed Color PSD: %d channels/%d bits", header.channels, header.bits)
|
|
||||||
);
|
|
||||||
|
|
||||||
case PSD.COLOR_MODE_DUOTONE:
|
case PSD.COLOR_MODE_DUOTONE:
|
||||||
// NOTE: Duotone (whatever that is) should be treated as gray scale
|
// NOTE: Duotone (whatever that is) should be treated as gray scale
|
||||||
// Fall-through
|
// Fall-through
|
||||||
case PSD.COLOR_MODE_GRAYSCALE:
|
case PSD.COLOR_MODE_GRAYSCALE:
|
||||||
|
cs = getEmbeddedColorSpace();
|
||||||
|
if (cs == null) {
|
||||||
|
cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
|
||||||
|
}
|
||||||
|
|
||||||
if (header.channels == 1 && header.bits == 8) {
|
if (header.channels == 1 && header.bits == 8) {
|
||||||
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
|
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) {
|
else if (header.channels == 1 && header.bits == 16) {
|
||||||
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_USHORT_GRAY);
|
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(
|
throw new IIOException(String.format("Unsupported channel count/bit depth for Gray Scale PSD: %d channels/%d bits", header.channels, header.bits));
|
||||||
String.format("Unsupported channel count/bit depth for Gray Scale PSD: %d channels/%d bits", header.channels, header.bits)
|
|
||||||
);
|
|
||||||
|
|
||||||
case PSD.COLOR_MODE_RGB:
|
case PSD.COLOR_MODE_RGB:
|
||||||
cs = getEmbeddedColorSpace();
|
cs = getEmbeddedColorSpace();
|
||||||
if (cs == null) {
|
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);
|
||||||
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); // ColorSpaces.getColorSpace(ColorSpaces.CS_ADOBE_RGB_1998); ?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.channels == 3 && header.bits == 8) {
|
if (header.channels == 3 && header.bits == 8) {
|
||||||
@@ -208,10 +205,14 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
else if (header.channels >= 4 && header.bits == 16) {
|
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);
|
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(
|
throw new IIOException(String.format("Unsupported channel count/bit depth for RGB PSD: %d channels/%d bits", header.channels, header.bits));
|
||||||
String.format("Unsupported channel count/bit depth for RGB PSD: %d channels/%d bits", header.channels, header.bits)
|
|
||||||
);
|
|
||||||
|
|
||||||
case PSD.COLOR_MODE_CMYK:
|
case PSD.COLOR_MODE_CMYK:
|
||||||
cs = getEmbeddedColorSpace();
|
cs = getEmbeddedColorSpace();
|
||||||
@@ -231,10 +232,14 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
else if (header.channels == 5 && header.bits == 16) {
|
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);
|
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(
|
throw new IIOException(String.format("Unsupported channel count/bit depth for CMYK PSD: %d channels/%d bits", header.channels, header.bits));
|
||||||
String.format("Unsupported channel count/bit depth for CMYK PSD: %d channels/%d bits", header.channels, header.bits)
|
|
||||||
);
|
|
||||||
|
|
||||||
case PSD.COLOR_MODE_MULTICHANNEL:
|
case PSD.COLOR_MODE_MULTICHANNEL:
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
@@ -242,9 +247,7 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
// TODO: If there's a color profile embedded, it should be easy, otherwise we're out of luck...
|
// TODO: If there's a color profile embedded, it should be easy, otherwise we're out of luck...
|
||||||
default:
|
default:
|
||||||
throw new IIOException(
|
throw new IIOException(String.format("Unsupported PSD MODE: %s (%d channels/%d bits)", header.mode, header.channels, header.bits));
|
||||||
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 {
|
private ColorSpace getEmbeddedColorSpace() throws IOException {
|
||||||
readImageResources(true);
|
readImageResources(true);
|
||||||
// TODO: Skip this, requires storing some stream offsets
|
|
||||||
readLayerAndMaskInfo(false);
|
|
||||||
|
|
||||||
if (colorSpace == null) {
|
if (colorSpace == null) {
|
||||||
ICC_Profile profile = 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 {
|
public BufferedImage read(final int imageIndex, final ImageReadParam param) throws IOException {
|
||||||
checkBounds(imageIndex);
|
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?
|
// 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...
|
// 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: Support layer in index 0, if "has real merged data" flag is false?
|
||||||
// TODO: Param support in layer code (more duping/cleanup..)
|
// TODO: Param support in layer code (more duping/cleanup..)
|
||||||
if (imageIndex > 0) {
|
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);
|
return readLayerData(imageIndex - 1, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,13 +407,11 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
ySub = param.getSourceYSubsampling();
|
ySub = param.getSourceYSubsampling();
|
||||||
}
|
}
|
||||||
|
|
||||||
processImageStarted(imageIndex);
|
imageInput.seek(metadata.imageDataStart);
|
||||||
|
|
||||||
int[] byteCounts = null;
|
|
||||||
int compression = imageInput.readShort();
|
int compression = imageInput.readShort();
|
||||||
// TODO: Need to make sure compression is set in metadata, even without reading the image data!
|
|
||||||
metadata.compression = compression;
|
metadata.compression = compression;
|
||||||
|
|
||||||
|
int[] byteCounts = null;
|
||||||
switch (compression) {
|
switch (compression) {
|
||||||
case PSD.COMPRESSION_NONE:
|
case PSD.COMPRESSION_NONE:
|
||||||
break;
|
break;
|
||||||
@@ -435,14 +419,12 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
// NOTE: Byte counts will allow us to easily skip rows before AOI
|
// NOTE: Byte counts will allow us to easily skip rows before AOI
|
||||||
byteCounts = new int[header.channels * header.height];
|
byteCounts = new int[header.channels * header.height];
|
||||||
for (int i = 0; i < byteCounts.length; i++) {
|
for (int i = 0; i < byteCounts.length; i++) {
|
||||||
byteCounts[i] = imageInput.readUnsignedShort();
|
byteCounts[i] = header.largeFormat ? imageInput.readInt() : imageInput.readUnsignedShort();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PSD.COMPRESSION_ZIP:
|
case PSD.COMPRESSION_ZIP:
|
||||||
// TODO: Could probably use the ZIPDecoder (DeflateDecoder) here..
|
|
||||||
case PSD.COMPRESSION_ZIP_PREDICTION:
|
case PSD.COMPRESSION_ZIP_PREDICTION:
|
||||||
// TODO: Look at TIFF prediction reading
|
// TODO: Could probably use the ZIPDecoder (DeflateDecoder) here.. Look at TIFF prediction reading
|
||||||
// Could be same as PNG prediction? Read up...
|
|
||||||
throw new IIOException("PSD with ZIP compression not supported");
|
throw new IIOException("PSD with ZIP compression not supported");
|
||||||
default:
|
default:
|
||||||
throw new IIOException(
|
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
|
// What we read here is the "composite layer" of the PSD file
|
||||||
readImageData(image, rawType.getColorModel(), source, dest, xSub, ySub, byteCounts, compression);
|
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();
|
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);
|
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;
|
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:
|
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()) {
|
if (abortRequested()) {
|
||||||
@@ -531,6 +519,67 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
processImageProgress(100f * channel / channelCount + 100f * y / (height * channelCount));
|
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,
|
private void read16bitChannel(final int pChannel, final int pChannelCount,
|
||||||
final DataBuffer pData, final int pBands, final int pBandOffset,
|
final DataBuffer pData, final int pBands, final int pBandOffset,
|
||||||
final ColorModel pSourceColorModel,
|
final ColorModel pSourceColorModel,
|
||||||
@@ -731,7 +780,7 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
private void decomposeAlpha(final ColorModel pModel, final DataBuffer pBuffer,
|
private void decomposeAlpha(final ColorModel pModel, final DataBuffer pBuffer,
|
||||||
final int pWidth, final int pHeight, final int pChannels) {
|
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?
|
// TODO: What about CMYK + alpha?
|
||||||
if (pModel.hasAlpha() && pModel.getColorSpace().getType() == ColorSpace.TYPE_RGB) {
|
if (pModel.hasAlpha() && pModel.getColorSpace().getType() == ColorSpace.TYPE_RGB) {
|
||||||
|
|
||||||
@@ -791,9 +840,16 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
private void readHeader() throws IOException {
|
private void readHeader() throws IOException {
|
||||||
assertInput();
|
assertInput();
|
||||||
|
|
||||||
if (header == null) {
|
if (header == null) {
|
||||||
header = new PSDHeader(imageInput);
|
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 = new PSDMetadata();
|
||||||
metadata.header = header;
|
metadata.header = header;
|
||||||
|
|
||||||
@@ -818,6 +874,8 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
imageInput.skipBytes(length);
|
imageInput.skipBytes(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metadata.imageResourcesStart = imageInput.getStreamPosition();
|
||||||
|
|
||||||
// Don't need the header again
|
// Don't need the header again
|
||||||
imageInput.flushBefore(imageInput.getStreamPosition());
|
imageInput.flushBefore(imageInput.getStreamPosition());
|
||||||
}
|
}
|
||||||
@@ -826,85 +884,104 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
// TODO: Flags or list of interesting resources to parse
|
// TODO: Flags or list of interesting resources to parse
|
||||||
// TODO: Obey ignoreMetadata
|
// TODO: Obey ignoreMetadata
|
||||||
private void readImageResources(final boolean pParseData) throws IOException {
|
private void readImageResources(final boolean pParseData) throws IOException {
|
||||||
// TODO: Avoid unnecessary stream repositioning
|
readHeader();
|
||||||
long pos = imageInput.getFlushedPosition();
|
|
||||||
imageInput.seek(pos);
|
|
||||||
|
|
||||||
long length = imageInput.readUnsignedInt();
|
if (pParseData || metadata.layerAndMaskInfoStart == 0) {
|
||||||
|
imageInput.seek(metadata.imageResourcesStart);
|
||||||
|
|
||||||
if (pParseData && length > 0) {
|
long imageResourcesLength = imageInput.readUnsignedInt();
|
||||||
if (metadata.imageResources == null) {
|
|
||||||
metadata.imageResources = new ArrayList<PSDImageResource>();
|
|
||||||
long expectedEnd = imageInput.getStreamPosition() + length;
|
|
||||||
|
|
||||||
while (imageInput.getStreamPosition() < expectedEnd) {
|
if (pParseData && imageResourcesLength > 0) {
|
||||||
// TODO: Have PSDImageResources defer actual parsing? (Just store stream offsets)
|
if (metadata.imageResources == null) {
|
||||||
PSDImageResource resource = PSDImageResource.read(imageInput);
|
metadata.imageResources = new ArrayList<PSDImageResource>();
|
||||||
metadata.imageResources.add(resource);
|
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) {
|
// TODO: We should now be able to flush input
|
||||||
throw new IIOException("Corrupt PSD document"); // ..or maybe just a bug in the reader.. ;-)
|
// 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: Flags or list of interesting resources to parse
|
||||||
// TODO: Obey ignoreMetadata
|
// TODO: Obey ignoreMetadata
|
||||||
private void readLayerAndMaskInfo(final boolean pParseData) throws IOException {
|
private void readLayerAndMaskInfo(final boolean pParseData) throws IOException {
|
||||||
// TODO: Make sure we are positioned correctly
|
readImageResources(false);
|
||||||
// TODO: Avoid unnecessary stream repositioning
|
|
||||||
long length = imageInput.readUnsignedInt();
|
|
||||||
if (pParseData && length > 0) {
|
|
||||||
long pos = imageInput.getStreamPosition();
|
|
||||||
|
|
||||||
long read;
|
if (pParseData || metadata.imageDataStart == 0) {
|
||||||
if (metadata.layerInfo == null) {
|
imageInput.seek(metadata.layerAndMaskInfoStart);
|
||||||
long layerInfoLength = imageInput.readUnsignedInt();
|
|
||||||
|
|
||||||
/*
|
long layerAndMaskInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt();
|
||||||
"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();
|
|
||||||
|
|
||||||
PSDLayerInfo[] layerInfos = new PSDLayerInfo[Math.abs(layers)];
|
// NOTE: The spec says that if this section is empty, the length should be 0.
|
||||||
for (int i = 0; i < layerInfos.length; i++) {
|
// Yet I have a PSB file that has size 12, and both contained lengths set to 0 (which
|
||||||
layerInfos[i] = new PSDLayerInfo(imageInput);
|
// 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;
|
// read = imageInput.getStreamPosition() - pos;
|
||||||
|
//
|
||||||
long diff = layerInfoLength - (read - 4); // - 4 for the layerInfoLength field itself
|
// long toSkip = layerAndMaskInfoLength - read;
|
||||||
// System.out.println("diff: " + diff);
|
// System.out.println("toSkip: " + toSkip);
|
||||||
imageInput.skipBytes(diff);
|
// imageInput.skipBytes(toSkip);
|
||||||
|
|
||||||
// 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;
|
metadata.imageDataStart = metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4);
|
||||||
|
|
||||||
long toSkip = length - read;
|
|
||||||
// System.out.println("toSkip: " + toSkip);
|
|
||||||
imageInput.skipBytes(toSkip);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Skip entire layer and mask section
|
|
||||||
imageInput.skipBytes(length);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -949,19 +1026,22 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
final boolean banded = raster.getDataBuffer().getNumBanks() > 1;
|
final boolean banded = raster.getDataBuffer().getNumBanks() > 1;
|
||||||
final int interleavedBands = banded ? 1 : raster.getNumBands();
|
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) {
|
for (PSDChannelInfo channelInfo : layerInfo.channelInfo) {
|
||||||
int compression = imageInput.readUnsignedShort();
|
int compression = imageInput.readUnsignedShort();
|
||||||
|
|
||||||
// Skip layer if we can't read it
|
// Skip layer if we can't read it
|
||||||
// channelId == -2 means "user supplied layer mask", whatever that is...
|
// channelId
|
||||||
if (width <= 0 || height <= 0 || channelInfo.channelId == -2 ||
|
// -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)
|
||||||
(compression != PSD.COMPRESSION_NONE && compression != PSD.COMPRESSION_RLE)) {
|
if (width <= 0 || height <= 0 || channelInfo.channelId < -1 ||
|
||||||
|
(compression != PSD.COMPRESSION_NONE && compression != PSD.COMPRESSION_RLE)) { // TODO: ZIP Compressions!
|
||||||
imageInput.skipBytes(channelInfo.length - 2);
|
imageInput.skipBytes(channelInfo.length - 2);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// 0 = red, 1 = green, etc
|
// 0 = red, 1 = green, etc
|
||||||
// -1 = transparency mask; -2 = user supplied layer mask
|
// -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 ? layerInfo.channelInfo.length - 1 : channelInfo.channelId;
|
int c = channelInfo.channelId == -1 ? rowRaster.getNumBands() - 1 : channelInfo.channelId;
|
||||||
|
|
||||||
// NOTE: For layers, byte counts are written per channel, while for the composite data
|
// 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.
|
// byte counts are written for all channels before the image data.
|
||||||
@@ -975,10 +1055,10 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
case PSD.COMPRESSION_RLE:
|
case PSD.COMPRESSION_RLE:
|
||||||
// If RLE, the the image data starts with the byte counts
|
// If RLE, the the image data starts with the byte counts
|
||||||
// for all the scan lines in the channel (LayerBottom-LayerTop), with
|
// 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];
|
byteCounts = new int[layerInfo.bottom - layerInfo.top];
|
||||||
for (int i = 0; i < byteCounts.length; i++) {
|
for (int i = 0; i < byteCounts.length; i++) {
|
||||||
byteCounts[i] = imageInput.readUnsignedShort();
|
byteCounts[i] = header.largeFormat ? imageInput.readInt() : imageInput.readUnsignedShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -998,11 +1078,18 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
byte[] row8 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
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;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IIOException(String.format("Unknown PSD bit depth: %s", header.bits));
|
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 layer has more channels than composite data, it's normally extra alpha...
|
||||||
if (layerInfo.channelInfo.length > compositeType.getNumBands()) {
|
if (layerInfo.channelInfo.length > compositeType.getNumBands()) {
|
||||||
// ...but, it could also be just the user mask...
|
// ...but, it could also be just one of the user masks...
|
||||||
boolean userMask = false;
|
int newBandNum = 0;
|
||||||
|
|
||||||
for (PSDChannelInfo channelInfo : layerInfo.channelInfo) {
|
for (PSDChannelInfo channelInfo : layerInfo.channelInfo) {
|
||||||
if (channelInfo.channelId == -2) {
|
// -2 = user supplied layer mask, -3 real user supplied layer mask (when both a user mask and a vector mask are present)
|
||||||
userMask = true;
|
if (channelInfo.channelId >= -1) {
|
||||||
break;
|
newBandNum++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int newBandNum = layerInfo.channelInfo.length - (userMask ? 1 : 0);
|
|
||||||
|
|
||||||
// If there really is more channels, then create new imageTypeSpec
|
// If there really is more channels, then create new imageTypeSpec
|
||||||
if (newBandNum > compositeType.getNumBands()) {
|
if (newBandNum > compositeType.getNumBands()) {
|
||||||
@@ -1056,30 +1143,28 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
/// Layer support
|
/// 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
|
@Override
|
||||||
public int getNumImages(boolean allowSearch) throws IOException {
|
public int getNumImages(boolean allowSearch) throws IOException {
|
||||||
// NOTE: Spec says this method should throw IllegalStateException if allowSearch && isSeekForwardOnly()
|
// 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?
|
// But that makes no sense for a format (like PSD) that does not need to search, right?
|
||||||
|
|
||||||
readHeader();
|
readLayerAndMaskInfo(true);
|
||||||
readImageResources(false);
|
|
||||||
readLayerAndMaskInfo(true); // TODO: Consider quicker reading of just the number of layers.
|
|
||||||
|
|
||||||
return metadata.layerInfo != null ? metadata.layerInfo.size() + 1 : 1; // TODO: Only plus one, if "has real merged data"?
|
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
|
/// Metadata support
|
||||||
// TODO
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getStreamMetadata() throws IOException {
|
public IIOMetadata getStreamMetadata() throws IOException {
|
||||||
@@ -1090,20 +1175,15 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||||
// TODO: Implement
|
|
||||||
checkBounds(imageIndex);
|
checkBounds(imageIndex);
|
||||||
|
|
||||||
readHeader();
|
|
||||||
readImageResources(true);
|
readImageResources(true);
|
||||||
readLayerAndMaskInfo(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.compression = imageInput.readShort();
|
||||||
|
|
||||||
// metadata.header = header;
|
|
||||||
// metadata.colorData = colorData;
|
|
||||||
// metadata.imageResources = imageResources;
|
|
||||||
|
|
||||||
return metadata; // TODO: clone if we change to mutable metadata
|
return metadata; // TODO: clone if we change to mutable metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1129,8 +1209,6 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
if (metadata.imageResources == null) {
|
if (metadata.imageResources == null) {
|
||||||
// TODO: Need flag here, to specify what resources to read...
|
// TODO: Need flag here, to specify what resources to read...
|
||||||
readImageResources(true);
|
readImageResources(true);
|
||||||
// TODO: Skip this, requires storing some stream offsets
|
|
||||||
readLayerAndMaskInfo(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PSDImageResource resource : metadata.imageResources) {
|
for (PSDImageResource resource : metadata.imageResources) {
|
||||||
@@ -1236,84 +1314,89 @@ public class PSDImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
PSDImageReader imageReader = new PSDImageReader(null);
|
PSDImageReader imageReader = new PSDImageReader(null);
|
||||||
|
|
||||||
File file = new File(pArgs[idx]);
|
for (; idx < pArgs.length; idx++) {
|
||||||
ImageInputStream stream = ImageIO.createImageInputStream(file);
|
File file = new File(pArgs[idx]);
|
||||||
imageReader.setInput(stream);
|
System.out.println();
|
||||||
|
System.out.println("file: " + file.getAbsolutePath());
|
||||||
|
|
||||||
imageReader.readHeader();
|
ImageInputStream stream = ImageIO.createImageInputStream(file);
|
||||||
// System.out.println("imageReader.header: " + imageReader.header);
|
imageReader.setInput(stream);
|
||||||
|
imageReader.readHeader();
|
||||||
|
System.out.println("imageReader.header: " + imageReader.header);
|
||||||
|
|
||||||
imageReader.readImageResources(true);
|
imageReader.readImageResources(true);
|
||||||
System.out.println("imageReader.imageResources: " + imageReader.metadata.imageResources);
|
System.out.println("imageReader.imageResources: " + imageReader.metadata.imageResources);
|
||||||
System.out.println();
|
System.out.println();
|
||||||
|
|
||||||
imageReader.readLayerAndMaskInfo(true);
|
imageReader.readLayerAndMaskInfo(true);
|
||||||
System.out.println("imageReader.layerInfo: " + imageReader.metadata.layerInfo);
|
System.out.println("imageReader.layerInfo: " + imageReader.metadata.layerInfo);
|
||||||
// System.out.println("imageReader.globalLayerMask: " + imageReader.globalLayerMask);
|
/*
|
||||||
System.out.println();
|
// System.out.println("imageReader.globalLayerMask: " + imageReader.globalLayerMask);
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
IIOMetadata metadata = imageReader.getImageMetadata(0);
|
IIOMetadata metadata = imageReader.getImageMetadata(0);
|
||||||
Node node;
|
Node node;
|
||||||
XMLSerializer serializer;
|
XMLSerializer serializer;
|
||||||
|
|
||||||
node = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
node = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||||
serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
|
serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
|
||||||
serializer.serialize(node, true);
|
serializer.serialize(node, true);
|
||||||
System.out.println();
|
System.out.println();
|
||||||
|
|
||||||
node = metadata.getAsTree(PSDMetadata.NATIVE_METADATA_FORMAT_NAME);
|
node = metadata.getAsTree(PSDMetadata.NATIVE_METADATA_FORMAT_NAME);
|
||||||
// serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
|
// serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
|
||||||
serializer.serialize(node, true);
|
serializer.serialize(node, true);
|
||||||
|
*/
|
||||||
if (readThumbnails && imageReader.hasThumbnails(0)) {
|
if (readThumbnails && imageReader.hasThumbnails(0)) {
|
||||||
int thumbnails = imageReader.getNumThumbnails(0);
|
int thumbnails = imageReader.getNumThumbnails(0);
|
||||||
for (int i = 0; i < thumbnails; i++) {
|
for (int i = 0; i < thumbnails; i++) {
|
||||||
showIt(imageReader.readThumbnail(0, i), String.format("Thumbnail %d", 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) {
|
if (sourceRegion != null) {
|
||||||
param.setSourceRegion(sourceRegion);
|
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()));
|
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
if (subsampleFactor > 1) {
|
||||||
image = ImageUtil.accelerate(image);
|
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);
|
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) {
|
showIt(image, file.getName());
|
||||||
int images = imageReader.getNumImages(true);
|
|
||||||
for (int i = 1; i < images; i++) {
|
if (readLayers) {
|
||||||
start = System.currentTimeMillis();
|
int images = imageReader.getNumImages(true);
|
||||||
BufferedImage layer = imageReader.read(i);
|
for (int i = 1; i < images; i++) {
|
||||||
System.out.println("layer read time: " + (System.currentTimeMillis() - start));
|
start = System.currentTimeMillis();
|
||||||
System.err.println("layer: " + layer);
|
BufferedImage layer = imageReader.read(i);
|
||||||
showIt(layer, "layer " + 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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDImageReaderSpi.java,v 1.0 Apr 29, 2008 4:49:03 PM haraldk Exp$
|
* @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}.
|
* Creates a {@code PSDImageReaderSpi}.
|
||||||
@@ -57,15 +57,18 @@ public class PSDImageReaderSpi extends ImageReaderSpi {
|
|||||||
super(
|
super(
|
||||||
providerInfo.getVendorName(),
|
providerInfo.getVendorName(),
|
||||||
providerInfo.getVersion(),
|
providerInfo.getVersion(),
|
||||||
new String[]{"psd", "PSD"},
|
new String[] {"psd", "PSD"},
|
||||||
new String[]{"psd"},
|
new String[] {"psd"},
|
||||||
new String[]{
|
new String[] {
|
||||||
"application/vnd.adobe.photoshop", // This one seems official, used in XMP
|
"image/vnd.adobe.photoshop", // Official, IANA registered
|
||||||
"image/x-psd", "application/x-photoshop", "image/x-photoshop"
|
"application/vnd.adobe.photoshop", // Used in XMP
|
||||||
|
"image/x-psd",
|
||||||
|
"application/x-photoshop",
|
||||||
|
"image/x-photoshop"
|
||||||
},
|
},
|
||||||
"com.twelvemkonkeys.imageio.plugins.psd.PSDImageReader",
|
"com.twelvemkonkeys.imageio.plugins.psd.PSDImageReader",
|
||||||
new Class[] {ImageInputStream.class},
|
new Class[] {ImageInputStream.class},
|
||||||
// new String[]{"com.twelvemkonkeys.imageio.plugins.psd.PSDImageWriterSpi"},
|
// new String[] {"com.twelvemkonkeys.imageio.plugins.psd.PSDImageWriterSpi"},
|
||||||
null,
|
null,
|
||||||
true, // supports standard stream metadata
|
true, // supports standard stream metadata
|
||||||
null, null, // native stream format name and class
|
null, null, // native stream format name and class
|
||||||
@@ -84,9 +87,23 @@ public class PSDImageReaderSpi extends ImageReaderSpi {
|
|||||||
ImageInputStream stream = (ImageInputStream) pSource;
|
ImageInputStream stream = (ImageInputStream) pSource;
|
||||||
|
|
||||||
stream.mark();
|
stream.mark();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return stream.readInt() == PSD.SIGNATURE_8BPS;
|
if (stream.readInt() == PSD.SIGNATURE_8BPS) {
|
||||||
// TODO: Test more of the header, see PSDImageReader#readHeader
|
int version = stream.readUnsignedShort();
|
||||||
|
|
||||||
|
switch (version) {
|
||||||
|
case PSD.VERSION_PSD:
|
||||||
|
case PSD.VERSION_PSB:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
stream.reset();
|
stream.reset();
|
||||||
|
|||||||
Regular → Executable
+14
-8
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2014, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -110,7 +110,7 @@ public class PSDImageResource {
|
|||||||
|
|
||||||
builder.append("[ID: 0x");
|
builder.append("[ID: 0x");
|
||||||
builder.append(Integer.toHexString(id));
|
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(name);
|
builder.append(name);
|
||||||
builder.append("\"");
|
builder.append("\"");
|
||||||
@@ -139,6 +139,13 @@ public class PSDImageResource {
|
|||||||
case PSD.RES_PRINT_FLAGS_INFORMATION:
|
case PSD.RES_PRINT_FLAGS_INFORMATION:
|
||||||
return null;
|
return null;
|
||||||
default:
|
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 {
|
try {
|
||||||
for (Field field : PSD.class.getDeclaredFields()) {
|
for (Field field : PSD.class.getDeclaredFields()) {
|
||||||
if (field.getName().startsWith("RES_") && field.getInt(null) == pId) {
|
if (field.getName().startsWith("RES_") && field.getInt(null) == pId) {
|
||||||
@@ -149,7 +156,7 @@ public class PSDImageResource {
|
|||||||
}
|
}
|
||||||
catch (IllegalAccessException ignore) {
|
catch (IllegalAccessException ignore) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return "UnknownResource";
|
return "UnknownResource";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,9 +167,9 @@ public class PSDImageResource {
|
|||||||
throw new IIOException(String.format("Wrong image resource type, expected '8BIM': '%s'", PSDUtil.intToStr(type)));
|
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,
|
// TODO: Have PSDImageResources defer actual parsing? (Just store stream offsets)
|
||||||
// version info, and thumbnail for thumbnail-support.
|
|
||||||
short id = pInput.readShort();
|
short id = pInput.readShort();
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case PSD.RES_RESOLUTION_INFO:
|
case PSD.RES_RESOLUTION_INFO:
|
||||||
return new PSDResolutionInfo(id, pInput);
|
return new PSDResolutionInfo(id, pInput);
|
||||||
@@ -196,9 +203,8 @@ public class PSDImageResource {
|
|||||||
case PSD.RES_PRINT_FLAGS_INFORMATION:
|
case PSD.RES_PRINT_FLAGS_INFORMATION:
|
||||||
return new PSDPrintFlagsInformation(id, pInput);
|
return new PSDPrintFlagsInformation(id, pInput);
|
||||||
default:
|
default:
|
||||||
if (id >= 0x07d0 && id <= 0x0bb6) {
|
if (id >= PSD.RES_PATH_INFO_MIN && id <= PSD.RES_PATH_INFO_MAX) {
|
||||||
// TODO: Parse saved path information
|
return new PSDPathResource(id, pInput);
|
||||||
return new PSDImageResource(id, pInput);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new PSDImageResource(id, pInput);
|
return new PSDImageResource(id, pInput);
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2014, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDLayerBlendMode.java,v 1.0 May 8, 2008 4:34:35 PM haraldk Exp$
|
* @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 blendMode;
|
||||||
final int opacity; // 0-255
|
final int opacity; // 0-255
|
||||||
final int clipping; // 0: base, 1: non-base
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDLayerInfo.java,v 1.0 Apr 29, 2008 6:01:12 PM haraldk Exp$
|
* @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 top;
|
||||||
final int left;
|
final int left;
|
||||||
final int bottom;
|
final int bottom;
|
||||||
@@ -52,7 +52,7 @@ class PSDLayerInfo {
|
|||||||
final PSDChannelSourceDestinationRange[] ranges;
|
final PSDChannelSourceDestinationRange[] ranges;
|
||||||
final String layerName;
|
final String layerName;
|
||||||
|
|
||||||
PSDLayerInfo(ImageInputStream pInput) throws IOException {
|
PSDLayerInfo(final boolean largeFormat, final ImageInputStream pInput) throws IOException {
|
||||||
top = pInput.readInt();
|
top = pInput.readInt();
|
||||||
left = pInput.readInt();
|
left = pInput.readInt();
|
||||||
bottom = pInput.readInt();
|
bottom = pInput.readInt();
|
||||||
@@ -63,7 +63,7 @@ class PSDLayerInfo {
|
|||||||
channelInfo = new PSDChannelInfo[channels];
|
channelInfo = new PSDChannelInfo[channels];
|
||||||
for (int i = 0; i < channels; i++) {
|
for (int i = 0; i < channels; i++) {
|
||||||
short channelId = pInput.readShort();
|
short channelId = pInput.readShort();
|
||||||
long length = pInput.readUnsignedInt();
|
long length = largeFormat ? pInput.readLong() : pInput.readUnsignedInt();
|
||||||
|
|
||||||
channelInfo[i] = new PSDChannelInfo(channelId, length);
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDLayerMaskData.java,v 1.0 May 6, 2008 5:15:05 PM haraldk Exp$
|
* @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 top;
|
||||||
private int left;
|
private int left;
|
||||||
private int bottom;
|
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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||||
@@ -35,7 +63,11 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
List<PSDImageResource> imageResources;
|
List<PSDImageResource> imageResources;
|
||||||
PSDGlobalLayerMask globalLayerMask;
|
PSDGlobalLayerMask globalLayerMask;
|
||||||
List<PSDLayerInfo> layerInfo;
|
List<PSDLayerInfo> layerInfo;
|
||||||
|
|
||||||
|
long imageResourcesStart;
|
||||||
|
long layerAndMaskInfoStart;
|
||||||
long layersStart;
|
long layersStart;
|
||||||
|
long imageDataStart;
|
||||||
|
|
||||||
static final String[] COLOR_MODES = {
|
static final String[] COLOR_MODES = {
|
||||||
"MONOCHROME", "GRAYSCALE", "INDEXED", "RGB", "CMYK", null, null, "MULTICHANNEL", "DUOTONE", "LAB"
|
"MONOCHROME", "GRAYSCALE", "INDEXED", "RGB", "CMYK", null, null, "MULTICHANNEL", "DUOTONE", "LAB"
|
||||||
@@ -85,17 +117,17 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Node createHeaderNode() {
|
private Node createHeaderNode() {
|
||||||
IIOMetadataNode header = new IIOMetadataNode("Header");
|
IIOMetadataNode headerNode = new IIOMetadataNode("Header");
|
||||||
|
|
||||||
header.setAttribute("type", "PSD");
|
headerNode.setAttribute("type", "PSD");
|
||||||
header.setAttribute("version", "1");
|
headerNode.setAttribute("version", header.largeFormat ? "2" : "1");
|
||||||
header.setAttribute("channels", Integer.toString(this.header.channels));
|
headerNode.setAttribute("channels", Integer.toString(header.channels));
|
||||||
header.setAttribute("height", Integer.toString(this.header.height));
|
headerNode.setAttribute("height", Integer.toString(header.height));
|
||||||
header.setAttribute("width", Integer.toString(this.header.width));
|
headerNode.setAttribute("width", Integer.toString(header.width));
|
||||||
header.setAttribute("bits", Integer.toString(this.header.bits));
|
headerNode.setAttribute("bits", Integer.toString(header.bits));
|
||||||
header.setAttribute("mode", COLOR_MODES[this.header.mode]);
|
headerNode.setAttribute("mode", COLOR_MODES[header.mode]);
|
||||||
|
|
||||||
return header;
|
return headerNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node createImageResourcesNode() {
|
private Node createImageResourcesNode() {
|
||||||
@@ -112,18 +144,6 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
// TODO: Format spec
|
// TODO: Format spec
|
||||||
node = new IIOMetadataNode("ICCProfile");
|
node = new IIOMetadataNode("ICCProfile");
|
||||||
node.setAttribute("colorSpaceType", JAVA_CS[profile.getProfile().getColorSpaceType()]);
|
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());
|
node.setUserObject(profile.getProfile());
|
||||||
}
|
}
|
||||||
else if (imageResource instanceof PSDAlphaChannelInfo) {
|
else if (imageResource instanceof PSDAlphaChannelInfo) {
|
||||||
@@ -347,13 +367,12 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IIOMetadataNode getStandardChromaNode() {
|
protected IIOMetadataNode getStandardChromaNode() {
|
||||||
IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma");
|
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||||
IIOMetadataNode node; // scratch node
|
|
||||||
|
|
||||||
node = new IIOMetadataNode("ColorSpaceType");
|
IIOMetadataNode colorSpaceType = new IIOMetadataNode("ColorSpaceType");
|
||||||
String cs;
|
String cs;
|
||||||
switch (header.mode) {
|
switch (header.mode) {
|
||||||
case PSD.COLOR_MODE_MONOCHROME:
|
case PSD.COLOR_MODE_BITMAP:
|
||||||
case PSD.COLOR_MODE_GRAYSCALE:
|
case PSD.COLOR_MODE_GRAYSCALE:
|
||||||
case PSD.COLOR_MODE_DUOTONE: // Rationale: Spec says treat as gray...
|
case PSD.COLOR_MODE_DUOTONE: // Rationale: Spec says treat as gray...
|
||||||
cs = "GRAY";
|
cs = "GRAY";
|
||||||
@@ -374,25 +393,24 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
default:
|
default:
|
||||||
throw new AssertionError("Unreachable");
|
throw new AssertionError("Unreachable");
|
||||||
}
|
}
|
||||||
node.setAttribute("name", cs);
|
colorSpaceType.setAttribute("name", cs);
|
||||||
chroma_node.appendChild(node);
|
chroma.appendChild(colorSpaceType);
|
||||||
|
|
||||||
// TODO: Channels might be 5 for RGB + A + Mask... Probably not correct
|
// TODO: Channels might be 5 for RGB + A + Mask... Probably not correct
|
||||||
node = new IIOMetadataNode("NumChannels");
|
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||||
node.setAttribute("value", Integer.toString(header.channels));
|
numChannels.setAttribute("value", Integer.toString(header.channels));
|
||||||
chroma_node.appendChild(node);
|
chroma.appendChild(numChannels);
|
||||||
|
|
||||||
// TODO: Check if this is correct with bitmap (monchrome)
|
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||||
node = new IIOMetadataNode("BlackIsZero");
|
blackIsZero.setAttribute("value", "true");
|
||||||
node.setAttribute("value", "true");
|
chroma.appendChild(blackIsZero);
|
||||||
chroma_node.appendChild(node);
|
|
||||||
|
|
||||||
if (header.mode == PSD.COLOR_MODE_INDEXED) {
|
if (header.mode == PSD.COLOR_MODE_INDEXED) {
|
||||||
node = createPaletteNode();
|
IIOMetadataNode paletteNode = createPaletteNode();
|
||||||
chroma_node.appendChild(node);
|
chroma.appendChild(paletteNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Hardcode background color to white?
|
// TODO: Use
|
||||||
// if (bKGD_present) {
|
// if (bKGD_present) {
|
||||||
// if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
|
// if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
|
||||||
// node = new IIOMetadataNode("BackgroundIndex");
|
// node = new IIOMetadataNode("BackgroundIndex");
|
||||||
@@ -415,24 +433,25 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
// chroma_node.appendChild(node);
|
// chroma_node.appendChild(node);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return chroma_node;
|
return chroma;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IIOMetadataNode createPaletteNode() {
|
private IIOMetadataNode createPaletteNode() {
|
||||||
IIOMetadataNode node = new IIOMetadataNode("Palette");
|
IIOMetadataNode palette = new IIOMetadataNode("Palette");
|
||||||
IndexColorModel cm = colorData.getIndexColorModel();
|
IndexColorModel cm = colorData.getIndexColorModel();
|
||||||
|
|
||||||
for (int i = 0; i < cm.getMapSize(); i++) {
|
for (int i = 0; i < cm.getMapSize(); i++) {
|
||||||
IIOMetadataNode entry = new IIOMetadataNode("PaletteEntry");
|
IIOMetadataNode entry = new IIOMetadataNode("PaletteEntry");
|
||||||
|
|
||||||
entry.setAttribute("index", Integer.toString(i));
|
entry.setAttribute("index", Integer.toString(i));
|
||||||
entry.setAttribute("red", Integer.toString(cm.getRed(i)));
|
entry.setAttribute("red", Integer.toString(cm.getRed(i)));
|
||||||
entry.setAttribute("green", Integer.toString(cm.getGreen(i)));
|
entry.setAttribute("green", Integer.toString(cm.getGreen(i)));
|
||||||
entry.setAttribute("blue", Integer.toString(cm.getBlue(i)));
|
entry.setAttribute("blue", Integer.toString(cm.getBlue(i)));
|
||||||
|
|
||||||
node.appendChild(entry);
|
palette.appendChild(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return palette;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMultiChannelCS(short channels) {
|
private String getMultiChannelCS(short channels) {
|
||||||
@@ -446,33 +465,33 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
@Override
|
@Override
|
||||||
protected IIOMetadataNode getStandardCompressionNode() {
|
protected IIOMetadataNode getStandardCompressionNode() {
|
||||||
IIOMetadataNode compressionNode = new IIOMetadataNode("Compression");
|
IIOMetadataNode compressionNode = new IIOMetadataNode("Compression");
|
||||||
IIOMetadataNode node; // scratch node
|
|
||||||
|
|
||||||
node = new IIOMetadataNode("CompressionTypeName");
|
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||||
String compression;
|
String compressionName;
|
||||||
|
|
||||||
switch (this.compression) {
|
switch (compression) {
|
||||||
case PSD.COMPRESSION_NONE:
|
case PSD.COMPRESSION_NONE:
|
||||||
compression = "none";
|
compressionName = "none";
|
||||||
break;
|
break;
|
||||||
case PSD.COMPRESSION_RLE:
|
case PSD.COMPRESSION_RLE:
|
||||||
compression = "PackBits";
|
compressionName = "PackBits";
|
||||||
break;
|
break;
|
||||||
case PSD.COMPRESSION_ZIP:
|
case PSD.COMPRESSION_ZIP:
|
||||||
case PSD.COMPRESSION_ZIP_PREDICTION:
|
case PSD.COMPRESSION_ZIP_PREDICTION:
|
||||||
compression = "Deflate"; // TODO: ZLib? (TIFF native metadata format specifies both.. :-P)
|
compressionName = "Zip";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new AssertionError("Unreachable");
|
throw new AssertionError("Unreachable");
|
||||||
}
|
}
|
||||||
|
|
||||||
node.setAttribute("value", compression);
|
compressionTypeName.setAttribute("value", compressionName);
|
||||||
compressionNode.appendChild(node);
|
compressionNode.appendChild(compressionTypeName);
|
||||||
|
|
||||||
// TODO: Does it make sense to specify lossless for compression "none"?
|
if (compression != PSD.COMPRESSION_NONE) {
|
||||||
node = new IIOMetadataNode("Lossless");
|
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
||||||
node.setAttribute("value", "true");
|
lossless.setAttribute("value", "true");
|
||||||
compressionNode.appendChild(node);
|
compressionNode.appendChild(lossless);
|
||||||
|
}
|
||||||
|
|
||||||
return compressionNode;
|
return compressionNode;
|
||||||
}
|
}
|
||||||
@@ -480,15 +499,14 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
@Override
|
@Override
|
||||||
protected IIOMetadataNode getStandardDataNode() {
|
protected IIOMetadataNode getStandardDataNode() {
|
||||||
IIOMetadataNode dataNode = new IIOMetadataNode("Data");
|
IIOMetadataNode dataNode = new IIOMetadataNode("Data");
|
||||||
IIOMetadataNode node; // scratch node
|
|
||||||
|
|
||||||
node = new IIOMetadataNode("PlanarConfiguration");
|
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||||
node.setAttribute("value", "PlaneInterleaved"); // TODO: Check with spec
|
planarConfiguration.setAttribute("value", "PlaneInterleaved");
|
||||||
dataNode.appendChild(node);
|
dataNode.appendChild(planarConfiguration);
|
||||||
|
|
||||||
node = new IIOMetadataNode("SampleFormat");
|
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||||
node.setAttribute("value", header.mode == PSD.COLOR_MODE_INDEXED ? "Index" : "UnsignedIntegral");
|
sampleFormat.setAttribute("value", header.mode == PSD.COLOR_MODE_INDEXED ? "Index" : "UnsignedIntegral");
|
||||||
dataNode.appendChild(node);
|
dataNode.appendChild(sampleFormat);
|
||||||
|
|
||||||
String bitDepth = Integer.toString(header.bits); // bits per plane
|
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];
|
String[] bps = new String[header.channels];
|
||||||
Arrays.fill(bps, bitDepth);
|
Arrays.fill(bps, bitDepth);
|
||||||
|
|
||||||
node = new IIOMetadataNode("BitsPerSample");
|
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||||
node.setAttribute("value", StringUtil.toCSVString(bps, " "));
|
bitsPerSample.setAttribute("value", StringUtil.toCSVString(bps, " "));
|
||||||
dataNode.appendChild(node);
|
dataNode.appendChild(bitsPerSample);
|
||||||
|
|
||||||
// TODO: SampleMSB? Or is network (aka Motorola/big endian) byte order assumed?
|
|
||||||
|
|
||||||
return dataNode;
|
return dataNode;
|
||||||
}
|
}
|
||||||
@@ -542,33 +558,6 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
dimensionNode.appendChild(node);
|
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;
|
return dimensionNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -580,11 +569,10 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
@Override
|
@Override
|
||||||
protected IIOMetadataNode getStandardDocumentNode() {
|
protected IIOMetadataNode getStandardDocumentNode() {
|
||||||
IIOMetadataNode document_node = new IIOMetadataNode("Document");
|
IIOMetadataNode document_node = new IIOMetadataNode("Document");
|
||||||
IIOMetadataNode node; // scratch node
|
|
||||||
|
|
||||||
node = new IIOMetadataNode("FormatVersion");
|
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
|
||||||
node.setAttribute("value", "1"); // PSD format version is always 1
|
formatVersion.setAttribute("value", header.largeFormat ? "2" : "1"); // PSD format version is always 1, PSB is 2
|
||||||
document_node.appendChild(node);
|
document_node.appendChild(formatVersion);
|
||||||
|
|
||||||
// Get EXIF data if present
|
// Get EXIF data if present
|
||||||
Iterator<PSDEXIF1Data> exif = getResources(PSDEXIF1Data.class);
|
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
|
// Get the EXIF DateTime (aka ModifyDate) tag if present
|
||||||
Entry dateTime = data.directory.getEntryById(TIFF.TAG_DATE_TIME);
|
Entry dateTime = data.directory.getEntryById(TIFF.TAG_DATE_TIME);
|
||||||
if (dateTime != null) {
|
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"
|
// Format: "YYYY:MM:DD hh:mm:ss"
|
||||||
String value = dateTime.getValueAsString();
|
String value = dateTime.getValueAsString();
|
||||||
|
|
||||||
node.setAttribute("year", value.substring(0, 4));
|
imageCreationTime.setAttribute("year", value.substring(0, 4));
|
||||||
node.setAttribute("month", value.substring(5, 7));
|
imageCreationTime.setAttribute("month", value.substring(5, 7));
|
||||||
node.setAttribute("day", value.substring(8, 10));
|
imageCreationTime.setAttribute("day", value.substring(8, 10));
|
||||||
node.setAttribute("hour", value.substring(11, 13));
|
imageCreationTime.setAttribute("hour", value.substring(11, 13));
|
||||||
node.setAttribute("minute", value.substring(14, 16));
|
imageCreationTime.setAttribute("minute", value.substring(14, 16));
|
||||||
node.setAttribute("second", value.substring(17, 19));
|
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
|
@Override
|
||||||
protected IIOMetadataNode getStandardTextNode() {
|
protected IIOMetadataNode getStandardTextNode() {
|
||||||
// TODO: TIFF uses
|
// NOTE: TIFF uses
|
||||||
// DocumentName, ImageDescription, Make, Model, PageName, Software, Artist, HostComputer, InkNames, Copyright:
|
// DocumentName, ImageDescription, Make, Model, PageName, Software, Artist, HostComputer, InkNames, Copyright:
|
||||||
// /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value.
|
// /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value.
|
||||||
// Example: TIFF Software field => /Text/TextEntry@keyword = "Software",
|
// 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) {
|
private void appendTextEntriesFlat(final IIOMetadataNode node, final Directory directory, final FilterIterator.Filter<Entry> filter) {
|
||||||
FilterIterator<Entry> pEntries = new FilterIterator<Entry>(directory.iterator(), filter);
|
FilterIterator<Entry> entries = new FilterIterator<Entry>(directory.iterator(), filter);
|
||||||
while (pEntries.hasNext()) {
|
|
||||||
Entry entry = pEntries.next();
|
while (entries.hasNext()) {
|
||||||
|
Entry entry = entries.next();
|
||||||
|
|
||||||
if (entry.getValue() instanceof Directory) {
|
if (entry.getValue() instanceof Directory) {
|
||||||
appendTextEntriesFlat(node, (Directory) entry.getValue(), filter);
|
appendTextEntriesFlat(node, (Directory) entry.getValue(), filter);
|
||||||
@@ -694,7 +683,7 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
tag.setAttribute("keyword", fieldName);
|
tag.setAttribute("keyword", fieldName);
|
||||||
}
|
}
|
||||||
else {
|
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()));
|
tag.setAttribute("keyword", String.format("%s", entry.getIdentifier()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -711,14 +700,13 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||||
IIOMetadataNode transparency_node = new IIOMetadataNode("Transparency");
|
IIOMetadataNode transparencyNode = new IIOMetadataNode("Transparency");
|
||||||
IIOMetadataNode node; // scratch node
|
|
||||||
|
|
||||||
node = new IIOMetadataNode("Alpha");
|
IIOMetadataNode node = new IIOMetadataNode("Alpha");
|
||||||
node.setAttribute("value", hasAlpha() ? "nonpremultiplied" : "none"); // TODO: Check spec
|
node.setAttribute("value", hasAlpha() ? "premultiplied" : "none");
|
||||||
transparency_node.appendChild(node);
|
transparencyNode.appendChild(node);
|
||||||
|
|
||||||
return transparency_node;
|
return transparencyNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasAlpha() {
|
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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||||
@@ -17,7 +45,7 @@ import java.util.Arrays;
|
|||||||
*/
|
*/
|
||||||
public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
|
public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
|
||||||
|
|
||||||
private final static PSDMetadataFormat sInstance = new PSDMetadataFormat();
|
private static final PSDMetadataFormat instance = new PSDMetadataFormat();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor.
|
* Private constructor.
|
||||||
@@ -207,6 +235,6 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
|
|||||||
* @see javax.imageio.metadata.IIOMetadata#getMetadataFormat
|
* @see javax.imageio.metadata.IIOMetadata#getMetadataFormat
|
||||||
*/
|
*/
|
||||||
public static PSDMetadataFormat getInstance() {
|
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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
@@ -33,9 +61,13 @@ final class PSDPrintFlags extends PSDImageResource {
|
|||||||
negative = pInput.readBoolean();
|
negative = pInput.readBoolean();
|
||||||
flip = pInput.readBoolean();
|
flip = pInput.readBoolean();
|
||||||
interpolate = 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
|
@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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2014, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$
|
* @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
|
// typedef struct _ResolutionInfo
|
||||||
// {
|
// {
|
||||||
// LONG hRes; /* Fixed-point number: pixels per inch */
|
// 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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
@@ -16,7 +44,7 @@ import java.io.IOException;
|
|||||||
* @author last modified by $Author: haraldk$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PSDThumbnail.java,v 1.0 Jul 29, 2009 4:41:06 PM haraldk Exp$
|
* @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 format;
|
||||||
private int width;
|
private int width;
|
||||||
private int height;
|
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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2014, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
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;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||||
@@ -27,7 +55,7 @@ import static org.junit.Assert.*;
|
|||||||
*/
|
*/
|
||||||
public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImageReader> {
|
public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImageReader> {
|
||||||
|
|
||||||
static ImageReaderSpi provider = new PSDImageReaderSpi();
|
private static final ImageReaderSpi provider = new PSDImageReaderSpi();
|
||||||
|
|
||||||
protected List<TestData> getTestData() {
|
protected List<TestData> getTestData() {
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
@@ -48,7 +76,21 @@ public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImage
|
|||||||
// 1 channel, gray, 16 bit samples
|
// 1 channel, gray, 16 bit samples
|
||||||
new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)),
|
new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)),
|
||||||
// 4 channel, CMYK, 16 bit samples
|
// 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 uncompressed PSD
|
||||||
// TODO: Need more recent ZIP compressed PSD files from CS2/CS3+
|
// 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() {
|
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() {
|
public void testSupportsThumbnail() {
|
||||||
PSDImageReader imageReader = createReader();
|
PSDImageReader imageReader = createReader();
|
||||||
assertTrue(imageReader.readerSupportsThumbnails());
|
assertTrue(imageReader.readerSupportsThumbnails());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testThumbnailReading() throws IOException {
|
public void testThumbnailReading() throws IOException {
|
||||||
PSDImageReader imageReader = createReader();
|
PSDImageReader imageReader = createReader();
|
||||||
|
|
||||||
@@ -98,6 +146,7 @@ public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImage
|
|||||||
assertEquals(96, thumbnail.getHeight());
|
assertEquals(96, thumbnail.getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testThumbnailReadingNoInput() throws IOException {
|
public void testThumbnailReadingNoInput() throws IOException {
|
||||||
PSDImageReader imageReader = createReader();
|
PSDImageReader imageReader = createReader();
|
||||||
|
|
||||||
@@ -134,6 +183,7 @@ public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testThumbnailReadingOutOfBounds() throws IOException {
|
public void testThumbnailReadingOutOfBounds() throws IOException {
|
||||||
PSDImageReader imageReader = createReader();
|
PSDImageReader imageReader = createReader();
|
||||||
|
|
||||||
@@ -175,6 +225,7 @@ public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testThumbnailDimensions() throws IOException {
|
public void testThumbnailDimensions() throws IOException {
|
||||||
PSDImageReader imageReader = createReader();
|
PSDImageReader imageReader = createReader();
|
||||||
|
|
||||||
@@ -186,6 +237,7 @@ public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImage
|
|||||||
assertEquals(96, imageReader.getThumbnailHeight(0, 0));
|
assertEquals(96, imageReader.getThumbnailHeight(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testThumbnailReadListeners() throws IOException {
|
public void testThumbnailReadListeners() throws IOException {
|
||||||
PSDImageReader imageReader = createReader();
|
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>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-reference</artifactId>
|
<artifactId>imageio-reference</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: reference test cases</name>
|
<name>TwelveMonkeys :: ImageIO :: reference test cases</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-thumbsdb</artifactId>
|
<artifactId>imageio-thumbsdb</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: Thumbs.db plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: Thumbs.db plugin</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.0-rc5</version>
|
<version>3.0.3-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-tiff</artifactId>
|
<artifactId>imageio-tiff</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: TIFF plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: TIFF plugin</name>
|
||||||
|
|||||||
+2
-1
@@ -81,6 +81,7 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
|
|||||||
private void fetch() throws IOException {
|
private void fetch() throws IOException {
|
||||||
if (decodedPos >= decodedLength) {
|
if (decodedPos >= decodedLength) {
|
||||||
decodedLength = 0;
|
decodedLength = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
decodeRow();
|
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);
|
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 {
|
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;
|
private final ByteBuffer buffer;
|
||||||
|
|
||||||
public HorizontalDeDifferencingStream(final InputStream stream, final int columns, final int samplesPerPixel, final int bitsPerSample, final ByteOrder byteOrder) {
|
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.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.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");
|
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 = ByteBuffer.allocate((columns * samplesPerPixel * bitsPerSample + 7) / 8).order(byteOrder);
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
}
|
}
|
||||||
@@ -113,10 +113,15 @@ final class HorizontalDeDifferencingStream extends InputStream {
|
|||||||
int sample = 0;
|
int sample = 0;
|
||||||
byte temp;
|
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) {
|
switch (bitsPerSample) {
|
||||||
case 1:
|
case 1:
|
||||||
for (int b = 0; b < (columns + 7) / 8; b++) {
|
for (int b = 0; b < (columns + 7) / 8; b++) {
|
||||||
original = buffer.get(b);
|
original = array[b];
|
||||||
sample += (original >> 7) & 0x1;
|
sample += (original >> 7) & 0x1;
|
||||||
temp = (byte) ((sample << 7) & 0x80);
|
temp = (byte) ((sample << 7) & 0x80);
|
||||||
sample += (original >> 6) & 0x1;
|
sample += (original >> 6) & 0x1;
|
||||||
@@ -132,13 +137,13 @@ final class HorizontalDeDifferencingStream extends InputStream {
|
|||||||
sample += (original >> 1) & 0x1;
|
sample += (original >> 1) & 0x1;
|
||||||
temp |= (byte) ((sample << 1) & 0x02);
|
temp |= (byte) ((sample << 1) & 0x02);
|
||||||
sample += original & 0x1;
|
sample += original & 0x1;
|
||||||
buffer.put(b, (byte) (temp | sample & 0x1));
|
array[b] = (byte) (temp | sample & 0x1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
for (int b = 0; b < (columns + 3) / 4; b++) {
|
for (int b = 0; b < (columns + 3) / 4; b++) {
|
||||||
original = buffer.get(b);
|
original = array[b];
|
||||||
sample += (original >> 6) & 0x3;
|
sample += (original >> 6) & 0x3;
|
||||||
temp = (byte) ((sample << 6) & 0xc0);
|
temp = (byte) ((sample << 6) & 0xc0);
|
||||||
sample += (original >> 4) & 0x3;
|
sample += (original >> 4) & 0x3;
|
||||||
@@ -146,17 +151,17 @@ final class HorizontalDeDifferencingStream extends InputStream {
|
|||||||
sample += (original >> 2) & 0x3;
|
sample += (original >> 2) & 0x3;
|
||||||
temp |= (byte) ((sample << 2) & 0x0c);
|
temp |= (byte) ((sample << 2) & 0x0c);
|
||||||
sample += original & 0x3;
|
sample += original & 0x3;
|
||||||
buffer.put(b, (byte) (temp | sample & 0x3));
|
array[b] = (byte) (temp | sample & 0x3);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
for (int b = 0; b < (columns + 1) / 2; b++) {
|
for (int b = 0; b < (columns + 1) / 2; b++) {
|
||||||
original = buffer.get(b);
|
original = array[b];
|
||||||
sample += (original >> 4) & 0xf;
|
sample += (original >> 4) & 0xf;
|
||||||
temp = (byte) ((sample << 4) & 0xf0);
|
temp = (byte) ((sample << 4) & 0xf0);
|
||||||
sample += original & 0x0f;
|
sample += original & 0x0f;
|
||||||
buffer.put(b, (byte) (temp | sample & 0xf));
|
array[b] = (byte) (temp | sample & 0xf);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -164,7 +169,7 @@ final class HorizontalDeDifferencingStream extends InputStream {
|
|||||||
for (int x = 1; x < columns; x++) {
|
for (int x = 1; x < columns; x++) {
|
||||||
for (int b = 0; b < samplesPerPixel; b++) {
|
for (int b = 0; b < samplesPerPixel; b++) {
|
||||||
int off = x * 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;
|
break;
|
||||||
|
|||||||
+74
-31
@@ -36,8 +36,8 @@ import java.io.InputStream;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lempel–Ziv–Welch (LZW) decompression. LZW is a universal loss-less data compression algorithm
|
* Lempel–Ziv–Welch (LZW) decompression.
|
||||||
* created by Abraham Lempel, Jacob Ziv, and Terry Welch.
|
* LZW is a universal loss-less data compression algorithm created by Abraham Lempel, Jacob Ziv, and Terry Welch.
|
||||||
* Inspired by libTiff's LZW decompression.
|
* Inspired by libTiff's LZW decompression.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
@@ -56,9 +56,7 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
|
|
||||||
private static final int TABLE_SIZE = 1 << MAX_BITS;
|
private static final int TABLE_SIZE = 1 << MAX_BITS;
|
||||||
|
|
||||||
private final boolean compatibilityMode;
|
private final LZWString[] table;
|
||||||
|
|
||||||
private final String[] table;
|
|
||||||
private int tableLength;
|
private int tableLength;
|
||||||
int bitsPerCode;
|
int bitsPerCode;
|
||||||
private int oldCode = CLEAR_CODE;
|
private int oldCode = CLEAR_CODE;
|
||||||
@@ -69,15 +67,12 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
int nextData;
|
int nextData;
|
||||||
int nextBits;
|
int nextBits;
|
||||||
|
|
||||||
|
protected LZWDecoder(int tableSize) {
|
||||||
protected LZWDecoder(final boolean compatibilityMode) {
|
table = new LZWString[tableSize];
|
||||||
this.compatibilityMode = compatibilityMode;
|
|
||||||
|
|
||||||
table = new String[compatibilityMode ? TABLE_SIZE + 1024 : TABLE_SIZE]; // libTiff adds 1024 "for compatibility"...
|
|
||||||
|
|
||||||
// First 258 entries of table is always fixed
|
// First 258 entries of table is always fixed
|
||||||
for (int i = 0; i < 256; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
table[i] = new String((byte) i);
|
table[i] = new LZWString((byte) i);
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
@@ -96,6 +91,10 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
||||||
|
if (buffer == null) {
|
||||||
|
throw new NullPointerException("buffer == null"); // As per contract
|
||||||
|
}
|
||||||
|
|
||||||
// Adapted from the pseudo-code example found in the TIFF 6.0 Specification, 1992.
|
// Adapted from the pseudo-code example found in the TIFF 6.0 Specification, 1992.
|
||||||
// See Section 13: "LZW Compression"/"LZW Decoding", page 61+
|
// See Section 13: "LZW Compression"/"LZW Decoding", page 61+
|
||||||
int code;
|
int code;
|
||||||
@@ -112,12 +111,16 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
table[code].writeTo(buffer);
|
table[code].writeTo(buffer);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (table[oldCode] == null) {
|
||||||
|
throw new DecodeException(String.format("Corrupted TIFF LZW: code %d (table size: %d)", oldCode, tableLength));
|
||||||
|
}
|
||||||
|
|
||||||
if (isInTable(code)) {
|
if (isInTable(code)) {
|
||||||
table[code].writeTo(buffer);
|
table[code].writeTo(buffer);
|
||||||
addStringToTable(table[oldCode].concatenate(table[code].firstChar));
|
addStringToTable(table[oldCode].concatenate(table[code].firstChar));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String outString = table[oldCode].concatenate(table[oldCode].firstChar);
|
LZWString outString = table[oldCode].concatenate(table[oldCode].firstChar);
|
||||||
|
|
||||||
outString.writeTo(buffer);
|
outString.writeTo(buffer);
|
||||||
addStringToTable(outString);
|
addStringToTable(outString);
|
||||||
@@ -127,7 +130,7 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
oldCode = code;
|
oldCode = code;
|
||||||
|
|
||||||
if (buffer.remaining() < maxString + 1) {
|
if (buffer.remaining() < maxString + 1) {
|
||||||
// Buffer full, stop decoding for now
|
// Buffer (almost) full, stop decoding for now
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,19 +138,19 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
return buffer.position();
|
return buffer.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addStringToTable(final String string) throws IOException {
|
private void addStringToTable(final LZWString string) throws IOException {
|
||||||
|
if (tableLength > table.length) {
|
||||||
|
throw new DecodeException(String.format("TIFF LZW with more than %d bits per code encountered (table overflow)", MAX_BITS));
|
||||||
|
}
|
||||||
|
|
||||||
table[tableLength++] = string;
|
table[tableLength++] = string;
|
||||||
|
|
||||||
if (tableLength > maxCode) {
|
if (tableLength > maxCode) {
|
||||||
bitsPerCode++;
|
bitsPerCode++;
|
||||||
|
|
||||||
if (bitsPerCode > MAX_BITS) {
|
if (bitsPerCode > MAX_BITS) {
|
||||||
if (compatibilityMode) {
|
// Continue reading MAX_BITS (12 bit) length codes
|
||||||
bitsPerCode--;
|
bitsPerCode = MAX_BITS;
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new DecodeException(java.lang.String.format("TIFF LZW with more than %d bits per code encountered (table overflow)", MAX_BITS));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bitMask = bitmaskFor(bitsPerCode);
|
bitMask = bitmaskFor(bitsPerCode);
|
||||||
@@ -167,9 +170,9 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
|
|
||||||
protected abstract int getNextCode(final InputStream stream) throws IOException;
|
protected abstract int getNextCode(final InputStream stream) throws IOException;
|
||||||
|
|
||||||
|
|
||||||
static boolean isOldBitReversedStream(final InputStream stream) throws IOException {
|
static boolean isOldBitReversedStream(final InputStream stream) throws IOException {
|
||||||
stream.mark(2);
|
stream.mark(2);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int one = stream.read();
|
int one = stream.read();
|
||||||
int two = stream.read();
|
int two = stream.read();
|
||||||
@@ -185,10 +188,10 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
return oldBitReversedStream ? new LZWCompatibilityDecoder() : new LZWSpecDecoder();
|
return oldBitReversedStream ? new LZWCompatibilityDecoder() : new LZWSpecDecoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class LZWSpecDecoder extends LZWDecoder {
|
static final class LZWSpecDecoder extends LZWDecoder {
|
||||||
|
|
||||||
protected LZWSpecDecoder() {
|
protected LZWSpecDecoder() {
|
||||||
super(false);
|
super(TABLE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -237,7 +240,7 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
// compressed data will be identical whether it is an ‘II’ or ‘MM’ file."
|
// compressed data will be identical whether it is an ‘II’ or ‘MM’ file."
|
||||||
|
|
||||||
protected LZWCompatibilityDecoder() {
|
protected LZWCompatibilityDecoder() {
|
||||||
super(true);
|
super(TABLE_SIZE + 1024); // libTiff adds 1024 "for compatibility", this value seems to work fine...
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -279,26 +282,26 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class String {
|
static final class LZWString {
|
||||||
final String previous;
|
final LZWString previous;
|
||||||
|
|
||||||
final int length;
|
final int length;
|
||||||
final byte value;
|
final byte value;
|
||||||
final byte firstChar; // Copied forward for fast access
|
final byte firstChar; // Copied forward for fast access
|
||||||
|
|
||||||
public String(final byte code) {
|
public LZWString(final byte code) {
|
||||||
this(code, code, 1, null);
|
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.value = value;
|
||||||
this.firstChar = firstChar;
|
this.firstChar = firstChar;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.previous = previous;
|
this.previous = previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final String concatenate(final byte firstChar) {
|
public final LZWString concatenate(final byte firstChar) {
|
||||||
return new String(firstChar, this.firstChar, length + 1, this);
|
return new LZWString(firstChar, this.firstChar, length + 1, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void writeTo(final ByteBuffer buffer) {
|
public final void writeTo(final ByteBuffer buffer) {
|
||||||
@@ -310,7 +313,7 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
buffer.put(value);
|
buffer.put(value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String e = this;
|
LZWString e = this;
|
||||||
final int offset = buffer.position();
|
final int offset = buffer.position();
|
||||||
|
|
||||||
for (int i = length - 1; i >= 0; i--) {
|
for (int i = length - 1; i >= 0; i--) {
|
||||||
@@ -321,6 +324,46 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
buffer.position(offset + length);
|
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 EXTRASAMPLE_UNASSOCIATED_ALPHA = 2;
|
||||||
|
|
||||||
int PREDICTOR_NONE = 1;
|
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_LOGL = 32844;
|
||||||
int PHOTOMETRIC_LOGLUV = 32845;
|
int PHOTOMETRIC_LOGLUV = 32845;
|
||||||
|
|
||||||
/** DNG: CFA (Color Filter Array)*/
|
/** DNG: CFA (Color Filter Array). */
|
||||||
int PHOTOMETRIC_CFA = 32803;
|
int PHOTOMETRIC_CFA = 32803;
|
||||||
/** DNG: LinearRaw*/
|
/** DNG: LinearRaw. */
|
||||||
int PHOTOMETRIC_LINEAR_RAW = 34892;
|
int PHOTOMETRIC_LINEAR_RAW = 34892;
|
||||||
|
|
||||||
int SAMPLEFORMAT_COMPLEX_INT = 5;
|
int SAMPLEFORMAT_COMPLEX_INT = 5;
|
||||||
|
|||||||
+13
-2
@@ -42,7 +42,7 @@ interface TIFFExtension {
|
|||||||
int COMPRESSION_CCITT_T6 = 4;
|
int COMPRESSION_CCITT_T6 = 4;
|
||||||
/** LZW Compression. Was baseline, but moved to extension due to license issues in the LZW algorithm. */
|
/** LZW Compression. Was baseline, but moved to extension due to license issues in the LZW algorithm. */
|
||||||
int COMPRESSION_LZW = 5;
|
int COMPRESSION_LZW = 5;
|
||||||
/** Deprecated. For backwards compatibility only. */
|
/** Deprecated. For backwards compatibility only ("Old-style" JPEG). */
|
||||||
int COMPRESSION_OLD_JPEG = 6;
|
int COMPRESSION_OLD_JPEG = 6;
|
||||||
/** JPEG Compression (lossy). */
|
/** JPEG Compression (lossy). */
|
||||||
int COMPRESSION_JPEG = 7;
|
int COMPRESSION_JPEG = 7;
|
||||||
@@ -69,7 +69,18 @@ interface TIFFExtension {
|
|||||||
int YCBCR_POSITIONING_CENTERED = 1;
|
int YCBCR_POSITIONING_CENTERED = 1;
|
||||||
int YCBCR_POSITIONING_COSITED = 2;
|
int YCBCR_POSITIONING_COSITED = 2;
|
||||||
|
|
||||||
// "Old-style" JPEG (obsolete)
|
/** Deprecated. For backwards compatibility only ("Old-style" JPEG). */
|
||||||
int JPEG_PROC_BASELINE = 1;
|
int JPEG_PROC_BASELINE = 1;
|
||||||
|
/** Deprecated. For backwards compatibility only ("Old-style" JPEG). */
|
||||||
int JPEG_PROC_LOSSLESS = 14;
|
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
+297
-139
@@ -105,8 +105,6 @@ import java.util.zip.InflaterInputStream;
|
|||||||
*/
|
*/
|
||||||
public class TIFFImageReader extends ImageReaderBase {
|
public class TIFFImageReader extends ImageReaderBase {
|
||||||
// TODOs ImageIO basic functionality:
|
// TODOs ImageIO basic functionality:
|
||||||
// TODO: Subsampling (*tests should be failing*)
|
|
||||||
// TODO: Source region (*tests should be failing*)
|
|
||||||
// TODO: Thumbnail support
|
// TODO: Thumbnail support
|
||||||
// TODO: TIFFImageWriter + Spi
|
// TODO: TIFFImageWriter + Spi
|
||||||
|
|
||||||
@@ -134,6 +132,8 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
// Handle SampleFormat (and give up if not == 1)
|
// Handle SampleFormat (and give up if not == 1)
|
||||||
// Support Compression 6 ('Old-style' JPEG)
|
// Support Compression 6 ('Old-style' JPEG)
|
||||||
// Support Compression 2 (CCITT Modified Huffman RLE) for bi-level images
|
// 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"));
|
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);
|
Entry entry = currentIFD.getEntryById(tag);
|
||||||
|
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
if (defaultValue != null) {
|
if (defaultValue != null) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,6 +258,11 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
case 1:
|
case 1:
|
||||||
// TIFF 6.0 Spec says: 1, 4 or 8 for baseline (1 for bi-level, 4/8 for gray)
|
// 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
|
// 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);
|
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)) {
|
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:
|
case TIFFExtension.PHOTOMETRIC_YCBCR:
|
||||||
// JPEG reader will handle YCbCr to RGB for us, otherwise we'll convert while reading
|
// 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:
|
case TIFFBaseline.PHOTOMETRIC_RGB:
|
||||||
// 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);
|
cs = profile == null ? ColorSpace.getInstance(ColorSpace.CS_sRGB) : ColorSpaces.createColorSpace(profile);
|
||||||
|
|
||||||
switch (samplesPerPixel) {
|
switch (samplesPerPixel) {
|
||||||
@@ -330,24 +340,28 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
throw new IIOException("Missing ColorMap for Palette TIFF");
|
throw new IIOException("Missing ColorMap for Palette TIFF");
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] cmapShort = (int[]) colorMap.getValue();
|
IndexColorModel icm = createIndexColorModel(bitsPerSample, dataType, (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);
|
|
||||||
|
|
||||||
return IndexedImageTypeSpecifier.createFromIndexColorModel(icm);
|
return IndexedImageTypeSpecifier.createFromIndexColorModel(icm);
|
||||||
|
|
||||||
case TIFFExtension.PHOTOMETRIC_SEPARATED:
|
case TIFFExtension.PHOTOMETRIC_SEPARATED:
|
||||||
// Separated (CMYK etc)
|
// 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.
|
// 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);
|
cs = profile == null ? ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK) : ColorSpaces.createColorSpace(profile);
|
||||||
|
|
||||||
switch (samplesPerPixel) {
|
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 {
|
private int getSampleFormat() throws IIOException {
|
||||||
long[] value = getValueAsLongArray(TIFF.TAG_SAMPLE_FORMAT, "SampleFormat", false);
|
long[] value = getValueAsLongArray(TIFF.TAG_SAMPLE_FORMAT, "SampleFormat", false);
|
||||||
|
|
||||||
@@ -467,17 +518,20 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||||
checkReadParamBandSettings(param, rawType.getNumBands(), destination.getSampleModel().getNumBands());
|
checkReadParamBandSettings(param, rawType.getNumBands(), destination.getSampleModel().getNumBands());
|
||||||
|
|
||||||
final Rectangle source = new Rectangle();
|
final Rectangle srcRegion = new Rectangle();
|
||||||
final Rectangle dest = new Rectangle();
|
final Rectangle dstRegion = new Rectangle();
|
||||||
computeRegions(param, width, height, destination, source, dest);
|
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 interpretation = getValueAsInt(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, "PhotometricInterpretation");
|
||||||
final int compression = getValueAsIntWithDefault(TIFF.TAG_COMPRESSION, TIFFBaseline.COMPRESSION_NONE);
|
final int compression = getValueAsIntWithDefault(TIFF.TAG_COMPRESSION, TIFFBaseline.COMPRESSION_NONE);
|
||||||
final int predictor = getValueAsIntWithDefault(TIFF.TAG_PREDICTOR, 1);
|
final int predictor = getValueAsIntWithDefault(TIFF.TAG_PREDICTOR, 1);
|
||||||
final int planarConfiguration = getValueAsIntWithDefault(TIFF.TAG_PLANAR_CONFIGURATION, TIFFBaseline.PLANARCONFIG_CHUNKY);
|
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
|
// NOTE: We handle strips as tiles of tileWidth == width by tileHeight == rowsPerStrip
|
||||||
// Strips are top/down, tiles are left/right, top/down
|
// Strips are top/down, tiles are left/right, top/down
|
||||||
@@ -540,11 +594,11 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
double[] yCbCrCoefficients = null;
|
double[] yCbCrCoefficients = null;
|
||||||
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR) {
|
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR) {
|
||||||
// getRawImageType does the lookup/conversion for these
|
// getRawImageType does the lookup/conversion for these
|
||||||
if (raster.getNumBands() != 3) {
|
if (rowRaster.getNumBands() != 3) {
|
||||||
throw new IIOException("TIFF PhotometricInterpretation YCbCr requires SamplesPerPixel == 3: " + raster.getNumBands());
|
throw new IIOException("TIFF PhotometricInterpretation YCbCr requires SamplesPerPixel == 3: " + rowRaster.getNumBands());
|
||||||
}
|
}
|
||||||
if (raster.getTransferType() != DataBuffer.TYPE_BYTE) {
|
if (rowRaster.getTransferType() != DataBuffer.TYPE_BYTE && rowRaster.getTransferType() != DataBuffer.TYPE_USHORT) {
|
||||||
throw new IIOException("TIFF PhotometricInterpretation YCbCr requires BitsPerSample == [8,8,8]");
|
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);
|
yCbCrPos = getValueAsIntWithDefault(TIFF.TAG_YCBCR_POSITIONING, TIFFExtension.YCBCR_POSITIONING_CENTERED);
|
||||||
@@ -590,7 +644,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
// Read data
|
// Read data
|
||||||
processImageStarted(imageIndex);
|
processImageStarted(imageIndex);
|
||||||
|
|
||||||
// TODO: Read only tiles that lies within region
|
|
||||||
// General uncompressed/compressed reading
|
// General uncompressed/compressed reading
|
||||||
for (int y = 0; y < tilesDown; y++) {
|
for (int y = 0; y < tilesDown; y++) {
|
||||||
int col = 0;
|
int col = 0;
|
||||||
@@ -612,12 +665,19 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
? IIOUtil.createStreamAdapter(imageInput, stripTileByteCounts[i])
|
? IIOUtil.createStreamAdapter(imageInput, stripTileByteCounts[i])
|
||||||
: IIOUtil.createStreamAdapter(imageInput);
|
: IIOUtil.createStreamAdapter(imageInput);
|
||||||
|
|
||||||
adapter = createDecompressorStream(compression, width, adapter);
|
adapter = createDecompressorStream(compression, stripTileWidth, numBands, adapter);
|
||||||
adapter = createUnpredictorStream(predictor, width, planarConfiguration == 2 ? 1 : raster.getNumBands(), getBitsPerSample(), adapter, imageInput.getByteOrder());
|
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);
|
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
|
// According to the spec, short/long/etc should follow order of containing stream
|
||||||
input = imageInput.getByteOrder() == ByteOrder.BIG_ENDIAN
|
input = imageInput.getByteOrder() == ByteOrder.BIG_ENDIAN
|
||||||
@@ -625,8 +685,15 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
: new LittleEndianDataInputStream(adapter);
|
: 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
|
// 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()) {
|
if (abortRequested()) {
|
||||||
break;
|
break;
|
||||||
@@ -635,7 +702,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
col += colsInTile;
|
col += colsInTile;
|
||||||
}
|
}
|
||||||
|
|
||||||
processImageProgress(100f * row / (float) height);
|
processImageProgress(100f * row / height);
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
processReadAborted();
|
processReadAborted();
|
||||||
@@ -661,21 +728,14 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
Entry tablesEntry = currentIFD.getEntryById(TIFF.TAG_JPEG_TABLES);
|
Entry tablesEntry = currentIFD.getEntryById(TIFF.TAG_JPEG_TABLES);
|
||||||
byte[] tablesValue = tablesEntry != null ? (byte[]) tablesEntry.getValue() : null;
|
byte[] tablesValue = tablesEntry != null ? (byte[]) tablesEntry.getValue() : null;
|
||||||
if (tablesValue != 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
|
// Whatever values I pass the reader as the read param, it never gets the same quality as if
|
||||||
// I just invoke jpegReader.getStreamMetadata...
|
// I just invoke jpegReader.getStreamMetadata(), so we'll do that...
|
||||||
// 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..?
|
|
||||||
|
|
||||||
jpegReader.setInput(new ByteArrayImageInputStream(tablesValue));
|
jpegReader.setInput(new ByteArrayImageInputStream(tablesValue));
|
||||||
|
|
||||||
// NOTE: This initializes the tables and other internal settings for the reader (as if by magic).
|
// This initializes the tables and other internal settings for the reader,
|
||||||
// This is actually a feature of JPEG,
|
// and is actually a feature of JPEG, see abbreviated streams:
|
||||||
// see: http://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#abbrev
|
// http://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#abbrev
|
||||||
/*IIOMetadata streamMetadata = */jpegReader.getStreamMetadata();
|
jpegReader.getStreamMetadata();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
processWarningOccurred("Missing JPEGTables for TIFF with compression: 7 (JPEG)");
|
processWarningOccurred("Missing JPEGTables for TIFF with compression: 7 (JPEG)");
|
||||||
@@ -683,7 +743,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read data
|
// Read data
|
||||||
processImageStarted(imageIndex);
|
processImageStarted(imageIndex); // Better yet, would be to delegate read progress here...
|
||||||
|
|
||||||
for (int y = 0; y < tilesDown; y++) {
|
for (int y = 0; y < tilesDown; y++) {
|
||||||
int col = 0;
|
int col = 0;
|
||||||
@@ -693,20 +753,24 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
int i = y * tilesAcross + x;
|
int i = y * tilesAcross + x;
|
||||||
int colsInTile = Math.min(stripTileWidth, width - col);
|
int colsInTile = Math.min(stripTileWidth, width - col);
|
||||||
|
|
||||||
imageInput.seek(stripTileOffsets[i]);
|
// Read only tiles that lies within region
|
||||||
ImageInputStream subStream = new SubImageInputStream(imageInput, stripTileByteCounts != null ? (int) stripTileByteCounts[i] : Short.MAX_VALUE);
|
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()) {
|
if (abortRequested()) {
|
||||||
@@ -716,7 +780,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
col += colsInTile;
|
col += colsInTile;
|
||||||
}
|
}
|
||||||
|
|
||||||
processImageProgress(100f * row / (float) height);
|
processImageProgress(100f * row / height);
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
processReadAborted();
|
processReadAborted();
|
||||||
@@ -755,7 +819,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
int jpegLenght = getValueAsIntWithDefault(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, -1);
|
int jpegLenght = getValueAsIntWithDefault(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, -1);
|
||||||
// TODO: 515/JPEGRestartInterval (may be absent)
|
// TODO: 515/JPEGRestartInterval (may be absent)
|
||||||
|
|
||||||
// Currently ignored
|
// Currently ignored (for lossless only)
|
||||||
// 517/JPEGLosslessPredictors
|
// 517/JPEGLosslessPredictors
|
||||||
// 518/JPEGPointTransforms
|
// 518/JPEGPointTransforms
|
||||||
|
|
||||||
@@ -763,8 +827,9 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
if (jpegOffset != -1) {
|
if (jpegOffset != -1) {
|
||||||
// Straight forward case: We're good to go! We'll disregard tiling and any tables tags
|
// 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
|
||||||
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) {
|
|| 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.");
|
processWarningOccurred("Old-style JPEG compressed TIFF with JFIF stream encountered. Ignoring JPEG tables. Reading as single tile.");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -772,11 +837,38 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
imageInput.seek(jpegOffset);
|
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);
|
stream = new SubImageInputStream(imageInput, jpegLenght != -1 ? jpegLenght : Short.MAX_VALUE);
|
||||||
jpegReader.setInput(stream);
|
jpegReader.setInput(stream);
|
||||||
|
|
||||||
// Read data
|
// Read data
|
||||||
processImageStarted(imageIndex);
|
processImageStarted(imageIndex); // Better yet, would be to delegate read progress here...
|
||||||
|
|
||||||
try {
|
try {
|
||||||
jpegParam.setSourceRegion(new Rectangle(0, 0, width, height));
|
jpegParam.setSourceRegion(new Rectangle(0, 0, width, height));
|
||||||
@@ -797,7 +889,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// The hard way: Read tables and re-create a full JFIF stream
|
// 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.");
|
processWarningOccurred("Old-style JPEG compressed TIFF without JFIF stream encountered. Attempting to re-create JFIF stream.");
|
||||||
|
|
||||||
// 519/JPEGQTables
|
// 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
|
// 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.
|
// 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),
|
// 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
|
// 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 colsInTile = Math.min(stripTileWidth, width - col);
|
||||||
int i = y * tilesAcross + x;
|
int i = y * tilesAcross + x;
|
||||||
|
|
||||||
imageInput.seek(stripTileOffsets[i]);
|
// Read only tiles that lies within region
|
||||||
stream = ImageIO.createImageInputStream(new SequenceInputStream(Collections.enumeration(
|
if (new Rectangle(col, row, colsInTile, rowsInTile).intersects(srcRegion)) {
|
||||||
Arrays.asList(
|
imageInput.seek(stripTileOffsets[i]);
|
||||||
createJFIFStream(raster, stripTileWidth, stripTileHeight, qTables, dcTables, acTables),
|
stream = ImageIO.createImageInputStream(new SequenceInputStream(Collections.enumeration(
|
||||||
IIOUtil.createStreamAdapter(imageInput, stripTileByteCounts != null ? (int) stripTileByteCounts[i] : Short.MAX_VALUE),
|
Arrays.asList(
|
||||||
new ByteArrayInputStream(new byte[] {(byte) 0xff, (byte) 0xd9}) // EOI
|
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 {
|
try {
|
||||||
jpegParam.setSourceRegion(new Rectangle(0, 0, colsInTile, rowsInTile));
|
jpegParam.setSourceRegion(new Rectangle(0, 0, colsInTile, rowsInTile));
|
||||||
jpegParam.setDestinationOffset(new Point(col, row));
|
jpegParam.setDestinationOffset(new Point(col - srcRegion.x, row - srcRegion.y));
|
||||||
jpegParam.setDestination(destination);
|
jpegParam.setDestination(destination);
|
||||||
// TODO: This works only if Gray/YCbCr/RGB, not CMYK/LAB/etc...
|
// 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
|
// In the latter case we will have to use readAsRaster and do color conversion ourselves
|
||||||
jpegReader.read(0, jpegParam);
|
jpegReader.read(0, jpegParam);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
stream.close();
|
stream.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
@@ -890,7 +983,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
col += colsInTile;
|
col += colsInTile;
|
||||||
}
|
}
|
||||||
|
|
||||||
processImageProgress(100f * row / (float) height);
|
processImageProgress(100f * row / height);
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
processReadAborted();
|
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 {
|
private static InputStream createJFIFStream(WritableRaster raster, int stripTileWidth, int stripTileHeight, byte[][] qTables, byte[][] dcTables, byte[][] acTables) throws IOException {
|
||||||
FastByteArrayOutputStream stream = new FastByteArrayOutputStream(
|
FastByteArrayOutputStream stream = new FastByteArrayOutputStream(
|
||||||
2 + 2 + 2 + 6 + 3 * raster.getNumBands() +
|
2 + 2 + 2 + 6 + 3 * raster.getNumBands() +
|
||||||
5 * qTables.length + qTables.length * qTables[0].length +
|
5 * qTables.length + qTables.length * qTables[0].length +
|
||||||
5 * dcTables.length + dcTables.length * dcTables[0].length +
|
5 * dcTables.length + dcTables.length * dcTables[0].length +
|
||||||
5 * acTables.length + acTables.length * acTables[0].length +
|
5 * acTables.length + acTables.length * acTables[0].length +
|
||||||
8 + 2 * raster.getNumBands()
|
8 + 2 * raster.getNumBands()
|
||||||
);
|
);
|
||||||
|
|
||||||
DataOutputStream out = new DataOutputStream(stream);
|
DataOutputStream out = new DataOutputStream(stream);
|
||||||
@@ -1004,81 +1097,111 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
return stream.createInputStream();
|
return stream.createInputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readStripTileData(final WritableRaster rowRaster, final int interpretation,
|
private Raster clipRowToRect(final Raster raster, final Rectangle rect, final int[] bands, final int xSub) {
|
||||||
final WritableRaster raster, final int col, final int startRow,
|
if (rect.contains(raster.getMinX(), 0, raster.getWidth(), 1)
|
||||||
final int colsInStrip, final int rowsInStrip, final DataInput input)
|
&& 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 {
|
throws IOException {
|
||||||
switch (rowRaster.getTransferType()) {
|
|
||||||
|
switch (tileRowRaster.getTransferType()) {
|
||||||
case DataBuffer.TYPE_BYTE:
|
case DataBuffer.TYPE_BYTE:
|
||||||
byte[] rowData = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
byte[] rowDataByte = ((DataBufferByte) tileRowRaster.getDataBuffer()).getData();
|
||||||
|
|
||||||
for (int j = 0; j < rowsInStrip; j++) {
|
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||||
int row = startRow + j;
|
if (row >= srcRegion.y + srcRegion.height) {
|
||||||
|
break; // We're done with this tile
|
||||||
if (row >= raster.getHeight()) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input.readFully(rowData);
|
input.readFully(rowDataByte);
|
||||||
normalizeBlack(interpretation, rowData);
|
|
||||||
|
|
||||||
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
|
if (row % ySub == 0 && row >= srcRegion.y) {
|
||||||
raster.setDataElements(col, row, rowRaster);
|
normalizeBlack(interpretation, rowDataByte);
|
||||||
}
|
|
||||||
else if (col >= raster.getMinX() && col < raster.getWidth()) {
|
// Subsample horizontal
|
||||||
raster.setDataElements(col, row, rowRaster.createChild(0, 0, Math.min(colsInStrip, raster.getWidth() - col), 1, 0, 0, null));
|
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
|
// Else skip data
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case DataBuffer.TYPE_USHORT:
|
case DataBuffer.TYPE_USHORT:
|
||||||
short [] rowDataShort = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
|
short[] rowDataShort = ((DataBufferUShort) tileRowRaster.getDataBuffer()).getData();
|
||||||
|
|
||||||
for (int j = 0; j < rowsInStrip; j++) {
|
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||||
int row = startRow + j;
|
if (row >= srcRegion.y + srcRegion.height) {
|
||||||
|
break; // We're done with this tile
|
||||||
if (row >= raster.getHeight()) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int k = 0; k < rowDataShort.length; k++) {
|
readFully(input, rowDataShort);
|
||||||
rowDataShort[k] = input.readShort();
|
|
||||||
}
|
|
||||||
|
|
||||||
normalizeBlack(interpretation, rowDataShort);
|
if (row >= srcRegion.y) {
|
||||||
|
normalizeBlack(interpretation, rowDataShort);
|
||||||
|
|
||||||
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
|
// Subsample horizontal
|
||||||
raster.setDataElements(col, row, rowRaster);
|
if (xSub != 1) {
|
||||||
}
|
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||||
else if (col >= raster.getMinX() && col < raster.getWidth()) {
|
for (int b = 0; b < numBands; b++) {
|
||||||
raster.setDataElements(col, row, rowRaster.createChild(0, 0, Math.min(colsInStrip, raster.getWidth() - col), 1, 0, 0, null));
|
rowDataShort[x + b] = rowDataShort[x * xSub + b];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
raster.setDataElements(startCol, row - srcRegion.y, tileRowRaster);
|
||||||
}
|
}
|
||||||
// Else skip data
|
// Else skip data
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case DataBuffer.TYPE_INT:
|
case DataBuffer.TYPE_INT:
|
||||||
int [] rowDataInt = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
|
int[] rowDataInt = ((DataBufferInt) tileRowRaster.getDataBuffer()).getData();
|
||||||
|
|
||||||
for (int j = 0; j < rowsInStrip; j++) {
|
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||||
int row = startRow + j;
|
if (row >= srcRegion.y + srcRegion.height) {
|
||||||
|
break; // We're done with this tile
|
||||||
if (row >= raster.getHeight()) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int k = 0; k < rowDataInt.length; k++) {
|
readFully(input, rowDataInt);
|
||||||
rowDataInt[k] = input.readInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
normalizeBlack(interpretation, rowDataInt);
|
if (row >= srcRegion.y) {
|
||||||
|
normalizeBlack(interpretation, rowDataInt);
|
||||||
|
|
||||||
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
|
// Subsample horizontal
|
||||||
raster.setDataElements(col, row, rowRaster);
|
if (xSub != 1) {
|
||||||
}
|
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||||
else if (col >= raster.getMinX() && col < raster.getWidth()) {
|
for (int b = 0; b < numBands; b++) {
|
||||||
raster.setDataElements(col, row, rowRaster.createChild(0, 0, Math.min(colsInStrip, raster.getWidth() - col), 1, 0, 0, null));
|
rowDataInt[x + b] = rowDataInt[x * xSub + b];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
raster.setDataElements(startCol, row - srcRegion.y, tileRowRaster);
|
||||||
}
|
}
|
||||||
// Else skip data
|
// 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) {
|
private void normalizeBlack(int photometricInterpretation, short[] data) {
|
||||||
if (photometricInterpretation == TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO) {
|
if (photometricInterpretation == TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO) {
|
||||||
// Inverse values
|
// Inverse values
|
||||||
@@ -1114,14 +1263,14 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream createDecompressorStream(final int compression, final int width, final InputStream stream) throws IOException {
|
private InputStream createDecompressorStream(final int compression, final int width, final int bands, final InputStream stream) throws IOException {
|
||||||
switch (compression) {
|
switch (compression) {
|
||||||
case TIFFBaseline.COMPRESSION_NONE:
|
case TIFFBaseline.COMPRESSION_NONE:
|
||||||
return stream;
|
return stream;
|
||||||
case TIFFBaseline.COMPRESSION_PACKBITS:
|
case TIFFBaseline.COMPRESSION_PACKBITS:
|
||||||
return new DecoderStream(stream, new PackBitsDecoder(), 1024);
|
return new DecoderStream(stream, new PackBitsDecoder(), 1024);
|
||||||
case TIFFExtension.COMPRESSION_LZW:
|
case TIFFExtension.COMPRESSION_LZW:
|
||||||
return new DecoderStream(stream, LZWDecoder.create(LZWDecoder.isOldBitReversedStream(stream)), 1024);
|
return new DecoderStream(stream, LZWDecoder.create(LZWDecoder.isOldBitReversedStream(stream)), width * bands);
|
||||||
case TIFFExtension.COMPRESSION_ZLIB:
|
case TIFFExtension.COMPRESSION_ZLIB:
|
||||||
// TIFFphotoshop.pdf (aka TIFF specification, supplement 2) says ZLIB (8) and DEFLATE (32946) algorithms are identical
|
// TIFFphotoshop.pdf (aka TIFF specification, supplement 2) says ZLIB (8) and DEFLATE (32946) algorithms are identical
|
||||||
case TIFFExtension.COMPRESSION_DEFLATE:
|
case TIFFExtension.COMPRESSION_DEFLATE:
|
||||||
@@ -1280,13 +1429,16 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
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.setDestinationOffset(new Point(50, 150));
|
||||||
// param.setSourceSubsampling(2, 2, 0, 0);
|
// param.setSourceSubsampling(2, 2, 0, 0);
|
||||||
BufferedImage image = reader.read(imageNo, param);
|
BufferedImage image = reader.read(imageNo, param);
|
||||||
System.err.println("Read time: " + (System.currentTimeMillis() - start) + " ms");
|
System.err.println("Read time: " + (System.currentTimeMillis() - start) + " ms");
|
||||||
|
|
||||||
IIOMetadata metadata = reader.getImageMetadata(0);
|
IIOMetadata metadata = reader.getImageMetadata(imageNo);
|
||||||
if (metadata != null) {
|
if (metadata != null) {
|
||||||
new XMLSerializer(System.out, "UTF-8").serialize(metadata.getAsTree(metadata.getNativeMetadataFormatName()), false);
|
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");
|
// // 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)));
|
showIt(image, String.format("Image: %s [%d x %d]", file.getName(), reader.getWidth(imageNo), reader.getHeight(imageNo)));
|
||||||
|
|
||||||
try {
|
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;
|
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 <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haraldk$
|
* @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) {
|
private void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final double[] coefficients, final int offset) {
|
||||||
double y = (yCbCr[offset ] & 0xff);
|
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 cr = (yCbCr[offset + 2] & 0xff) - 128;
|
||||||
|
|
||||||
double lumaRed = coefficients[0];
|
double lumaRed = coefficients[0];
|
||||||
@@ -238,7 +238,7 @@ final class YCbCrUpsamplerStream extends FilterInputStream {
|
|||||||
|
|
||||||
int red = (int) Math.round(cr * (2 - 2 * lumaRed) + y);
|
int red = (int) Math.round(cr * (2 - 2 * lumaRed) + y);
|
||||||
int blue = (int) Math.round(cb * (2 - 2 * lumaBlue) + 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 ] = clamp(red);
|
||||||
rgb[offset + 2] = clamp(blue);
|
rgb[offset + 2] = clamp(blue);
|
||||||
@@ -342,10 +342,5 @@ final class YCbCrUpsamplerStream extends FilterInputStream {
|
|||||||
cmyk[offset + 2] = clamp(cmykY);
|
cmyk[offset + 2] = clamp(cmykY);
|
||||||
cmyk[offset + 3] = (byte) k; // K passes through unchanged
|
cmyk[offset + 3] = (byte) k; // K passes through unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
// private static byte clamp(int val) {
|
|
||||||
// return (byte) Math.max(0, Math.min(255, val));
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+108
-1
@@ -27,12 +27,24 @@ package com.twelvemonkeys.imageio.plugins.tiff;/*
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.imageio.ImageReadParam;
|
||||||
|
import javax.imageio.event.IIOReadWarningListener;
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.mockito.Matchers.contains;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TIFFImageReaderTest
|
* TIFFImageReaderTest
|
||||||
*
|
*
|
||||||
@@ -56,11 +68,15 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTestCase<TIFFImageRe
|
|||||||
new TestData(getClassLoaderResource("/tiff/bali.tif"), new Dimension(725, 489)), // Palette-based, LZW compressed
|
new TestData(getClassLoaderResource("/tiff/bali.tif"), new Dimension(725, 489)), // Palette-based, LZW compressed
|
||||||
new TestData(getClassLoaderResource("/tiff/f14.tif"), new Dimension(640, 480)), // Gray, uncompressed
|
new TestData(getClassLoaderResource("/tiff/f14.tif"), new Dimension(640, 480)), // Gray, uncompressed
|
||||||
new TestData(getClassLoaderResource("/tiff/marbles.tif"), new Dimension(1419, 1001)), // RGB, LZW compressed w/predictor
|
new TestData(getClassLoaderResource("/tiff/marbles.tif"), new Dimension(1419, 1001)), // RGB, LZW compressed w/predictor
|
||||||
|
new TestData(getClassLoaderResource("/tiff/lzw-full-12-bit-table.tif"), new Dimension(874, 1240)), // Gray, LZW compressed, w/predictor
|
||||||
new TestData(getClassLoaderResource("/tiff/chifley_logo.tif"), new Dimension(591, 177)), // CMYK, uncompressed
|
new TestData(getClassLoaderResource("/tiff/chifley_logo.tif"), new Dimension(591, 177)), // CMYK, uncompressed
|
||||||
new TestData(getClassLoaderResource("/tiff/ycbcr-cat.tif"), new Dimension(250, 325)), // YCbCr, LZW compressed
|
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/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/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...
|
||||||
|
new TestData(getClassLoaderResource("/tiff/lzw-long-strings-sample.tif"), new Dimension(316, 173)) // RGBA, LZW compressed w/predictor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,5 +110,96 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTestCase<TIFFImageRe
|
|||||||
return Arrays.asList("image/tiff");
|
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
|
// 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.ByteArrayInputStream;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ public class YCbCrUpsamplerStreamTest {
|
|||||||
1, 2, 3, 4, 5, 6, 7, 8, 42, 96,
|
1, 2, 3, 4, 5, 6, 7, 8, 42, 96,
|
||||||
108, 109, 110, 111, 112, 113, 114, 115, 43, 97
|
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[] {
|
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,
|
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,
|
1, 2, 3, 4, 42, 96, 77,
|
||||||
112, 113, 114, 115, 43, 97, 43
|
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[] {
|
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
|
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,
|
1, 2, 3, 4, 42, 96, 77,
|
||||||
112, 113, 114, 115, 43, 97, 43
|
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[] {
|
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
|
0, -123, 0, 20, 71, 0, 0, -78, 90, 0, -24, 0, 0, -122, 0, 74, 125, 6, 0, -77, 91, 0, -78, 0
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user