Compare commits

...

16 Commits

Author SHA1 Message Date
Harald Kuhr caaececa06 [maven-release-plugin] prepare for next development iteration 2015-08-14 12:11:07 +02:00
Harald Kuhr ffd4731334 [maven-release-plugin] prepare release twelvemonkeys-3.1.2 2015-08-14 12:11:00 +02:00
Harald Kuhr 7c53c5b7b5 TMI-154: Fix for AccessControlException when reading "Generic CMYK.icc" 2015-08-14 11:51:27 +02:00
Harald Kuhr c9cfe04dc9 TMI-JPEG: Reading inverted Adobe JPEGs 2015-07-14 20:50:53 +02:00
Harald Kuhr 9da25168ee TMI-150: Fix for 24 bit images with bitmask. 2015-07-11 13:20:04 +02:00
Harald Kuhr b0c928d278 TMI-146: Now correctly sets ExtraSamples only when there are more components in the raster than color components in the color space. 2015-06-17 13:26:45 +02:00
Harald Kuhr e221c7374b [maven-release-plugin] prepare for next development iteration 2015-06-11 11:53:40 +02:00
Harald Kuhr 258a6cf4f4 [maven-release-plugin] prepare release twelvemonkeys-3.1.1 2015-06-11 11:53:35 +02:00
Harald Kuhr ab5cbb15ed TMI-142: Added missing resource file, now making sure the TIFFImageWriter is registered. 2015-06-08 15:37:20 +02:00
Harald Kuhr 02d65481c5 TMI-140: JPEG with corrupted ICC profile (new kind) can now be read. 2015-06-08 15:35:27 +02:00
Harald Kuhr 081c65b314 Preparing JPEGImageReader for extension. 2015-06-08 15:35:10 +02:00
Harald Kuhr 7a78d398c3 Fixed JavaDoc (you're welcome Sigi ;-)). 2015-06-08 15:31:55 +02:00
Harald Kuhr 493c304722 TM-138: DateUtil doesn't support sub-hour offset timezones. 2015-06-08 15:31:22 +02:00
Harald Kuhr 8d62d74860 TM-138: DateUtil doesn't support sub-hour offset timezones. 2015-06-08 15:31:06 +02:00
Harald Kuhr d51ac8cb83 TMI-134: Cannot read PSD images with PSD Layer Mask data size 28 2015-06-08 15:30:19 +02:00
Harald Kuhr 646ac719c1 Updating POM to version 3.1.1-SNAPSHOT 2015-06-08 14:18:49 +02:00
45 changed files with 531 additions and 201 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<groupId>com.twelvemonkeys.bom</groupId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>common-image</artifactId>
<packaging>jar</packaging>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>common-io</artifactId>
<packaging>jar</packaging>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>common-lang</artifactId>
<packaging>jar</packaging>
@@ -142,7 +142,7 @@ public final class DateUtil {
* @param pTime time
* @return the time rounded to the closest second.
*/
public static long roundToSecond(long pTime) {
public static long roundToSecond(final long pTime) {
return (pTime / SECOND) * SECOND;
}
@@ -152,7 +152,7 @@ public final class DateUtil {
* @param pTime time
* @return the time rounded to the closest minute.
*/
public static long roundToMinute(long pTime) {
public static long roundToMinute(final long pTime) {
return (pTime / MINUTE) * MINUTE;
}
@@ -162,9 +162,20 @@ public final class DateUtil {
* @param pTime time
* @return the time rounded to the closest hour.
*/
public static long roundToHour(long pTime) {
// TODO: What if timezone offset is sub hour? Are there any? I think so...
return ((pTime / HOUR) * HOUR);
public static long roundToHour(final long pTime) {
return roundToHour(pTime, TimeZone.getDefault());
}
/**
* Rounds the given time down to the closest hour, using the given timezone.
*
* @param pTime time
* @param pTimeZone the timezone to use when rounding
* @return the time rounded to the closest hour.
*/
public static long roundToHour(final long pTime, final TimeZone pTimeZone) {
int offset = pTimeZone.getOffset(pTime);
return ((pTime / HOUR) * HOUR) - offset;
}
/**
@@ -173,7 +184,7 @@ public final class DateUtil {
* @param pTime time
* @return the time rounded to the closest day.
*/
public static long roundToDay(long pTime) {
public static long roundToDay(final long pTime) {
return roundToDay(pTime, TimeZone.getDefault());
}
@@ -184,7 +195,7 @@ public final class DateUtil {
* @param pTimeZone the timezone to use when rounding
* @return the time rounded to the closest day.
*/
public static long roundToDay(long pTime, TimeZone pTimeZone) {
public static long roundToDay(final long pTime, final TimeZone pTimeZone) {
int offset = pTimeZone.getOffset(pTime);
return (((pTime + offset) / DAY) * DAY) - offset;
}
@@ -29,11 +29,15 @@
package com.twelvemonkeys.lang;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
/**
* DateUtilTest
@@ -42,9 +46,30 @@ import static org.junit.Assert.*;
* @author last modified by $Author: haraldk$
* @version $Id: DateUtilTest.java,v 1.0 11.04.12 16:21 haraldk Exp$
*/
@RunWith(Parameterized.class)
public class DateUtilTest {
private static Calendar getCalendar(long time) {
Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
private final TimeZone timeZone;
@Parameterized.Parameters
public static List<Object[]> timeZones() {
return Arrays.asList(new Object[][] {
{TimeZone.getTimeZone("UTC")},
{TimeZone.getTimeZone("CET")},
{TimeZone.getTimeZone("IST")}, // 30 min off
});
}
public DateUtilTest(final TimeZone timeZone) {
this.timeZone = timeZone;
}
private Calendar getCalendar(long time) {
return getCalendar(time, TimeZone.getDefault());
}
private Calendar getCalendar(long time, final TimeZone timeZone) {
Calendar calendar = Calendar.getInstance(timeZone);
calendar.setTimeInMillis(time);
return calendar;
@@ -74,6 +99,15 @@ public class DateUtilTest {
assertEquals(0, calendar.get(Calendar.MINUTE));
}
@Test
public void testRoundToHourTZ() {
Calendar calendar = getCalendar(DateUtil.roundToHour(System.currentTimeMillis(), timeZone), timeZone);
assertEquals(0, calendar.get(Calendar.MILLISECOND));
assertEquals(0, calendar.get(Calendar.SECOND));
assertEquals(0, calendar.get(Calendar.MINUTE));
}
@Test
public void testRoundToDay() {
Calendar calendar = getCalendar(DateUtil.roundToDay(System.currentTimeMillis()));
@@ -84,6 +118,16 @@ public class DateUtilTest {
assertEquals(0, calendar.get(Calendar.HOUR_OF_DAY));
}
@Test
public void testRoundToDayTZ() {
Calendar calendar = getCalendar(DateUtil.roundToDay(System.currentTimeMillis(), timeZone), timeZone);
assertEquals(0, calendar.get(Calendar.MILLISECOND));
assertEquals(0, calendar.get(Calendar.SECOND));
assertEquals(0, calendar.get(Calendar.MINUTE));
assertEquals(0, calendar.get(Calendar.HOUR_OF_DAY));
}
@Test
public void testCurrentTimeSecond() {
Calendar calendar = getCalendar(DateUtil.currentTimeSecond());
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-batik</artifactId>
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-bmp</artifactId>
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
@@ -43,6 +43,7 @@ abstract class BitmapDescriptor {
protected final DIBHeader header;
protected BufferedImage image;
protected BitmapMask mask;
public BitmapDescriptor(final DirectoryEntry pEntry, final DIBHeader pHeader) {
Validate.notNull(pEntry, "entry");
@@ -69,4 +70,17 @@ abstract class BitmapDescriptor {
protected final int getBitCount() {
return entry.getBitCount() != 0 ? entry.getBitCount() : header.getBitCount();
}
@Override
public String toString() {
return getClass().getSimpleName() + "[" + entry + ", " + header + "]";
}
public final void setMask(final BitmapMask mask) {
this.mask = mask;
}
public final boolean hasMask() {
return header.getHeight() == getHeight() * 2;
}
}
@@ -28,8 +28,6 @@
package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.image.InverseColorMapIndexColorModel;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
@@ -46,8 +44,6 @@ class BitmapIndexed extends BitmapDescriptor {
protected final int[] bits;
protected final int[] colors;
private BitmapMask mask;
public BitmapIndexed(final DirectoryEntry pEntry, final DIBHeader pHeader) {
super(pEntry, pHeader);
bits = new int[getWidth() * getHeight()];
@@ -65,7 +61,7 @@ class BitmapIndexed extends BitmapDescriptor {
// This is slightly obscure, and should probably be moved..
Hashtable<String, Object> properties = null;
if (entry instanceof DirectoryEntry.CUREntry) {
properties = new Hashtable<String, Object>(1);
properties = new Hashtable<>(1);
properties.put("cursor_hotspot", ((DirectoryEntry.CUREntry) this.entry).getHotspot());
}
@@ -89,8 +85,6 @@ class BitmapIndexed extends BitmapDescriptor {
raster.setSamples(0, 0, getWidth(), getHeight(), 0, bits);
//System.out.println("Image: " + image);
return image;
}
@@ -100,40 +94,40 @@ class BitmapIndexed extends BitmapDescriptor {
IndexColorModel createColorModel() {
// NOTE: This is a hack to make room for transparent pixel for mask
int bits = getBitCount();
int colors = this.colors.length;
int trans = -1;
int transparent = -1;
// Try to avoid USHORT transfertype, as it results in BufferedImage TYPE_CUSTOM
// NOTE: This code assumes icons are small, and is NOT optimized for performance...
if (colors > (1 << getBitCount())) {
int index = findTransIndexMaybeRemap(this.colors, this.bits);
int index = findTransparentIndexMaybeRemap(this.colors, this.bits);
if (index == -1) {
// No duplicate found, increase bitcount
bits++;
trans = this.colors.length - 1;
transparent = this.colors.length - 1;
}
else {
// Found a duplicate, use it as trans
trans = index;
// Found a duplicate, use it as transparent
transparent = index;
colors--;
}
}
// NOTE: Setting hasAlpha to true, makes things work on 1.2
return new InverseColorMapIndexColorModel(
bits, colors, this.colors, 0, true, trans,
return new IndexColorModel(
bits, colors, this.colors, 0, true, transparent,
bits <= 8 ? DataBuffer.TYPE_BYTE : DataBuffer.TYPE_USHORT
);
}
private static int findTransIndexMaybeRemap(final int[] pColors, final int[] pBits) {
private static int findTransparentIndexMaybeRemap(final int[] colors, final int[] bits) {
// Look for unused colors, to use as transparent
final boolean[] used = new boolean[pColors.length - 1];
for (int pBit : pBits) {
if (!used[pBit]) {
used[pBit] = true;
boolean[] used = new boolean[colors.length - 1];
for (int bit : bits) {
if (!used[bit]) {
used[bit] = true;
}
}
@@ -144,38 +138,35 @@ class BitmapIndexed extends BitmapDescriptor {
}
// Try to find duplicates in colormap, and remap
int trans = -1;
int transparent = -1;
int duplicate = -1;
for (int i = 0; trans == -1 && i < pColors.length - 1; i++) {
for (int j = i + 1; j < pColors.length - 1; j++) {
if (pColors[i] == pColors[j]) {
trans = j;
for (int i = 0; transparent == -1 && i < colors.length - 1; i++) {
for (int j = i + 1; j < colors.length - 1; j++) {
if (colors[i] == colors[j]) {
transparent = j;
duplicate = i;
break;
}
}
}
if (trans != -1) {
if (transparent != -1) {
// Remap duplicate
for (int i = 0; i < pBits.length; i++) {
if (pBits[i] == trans) {
pBits[i] = duplicate;
for (int i = 0; i < bits.length; i++) {
if (bits[i] == transparent) {
bits[i] = duplicate;
}
}
}
return trans;
return transparent;
}
public BufferedImage getImage() {
if (image == null) {
image = createImageIndexed();
}
return image;
}
public void setMask(final BitmapMask pMask) {
mask = pMask;
}
}
@@ -38,19 +38,19 @@ import java.awt.image.BufferedImage;
* @version $Id: BitmapMask.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
class BitmapMask extends BitmapDescriptor {
protected final BitmapIndexed mask;
protected final BitmapIndexed bitMask;
public BitmapMask(final DirectoryEntry pParent, final DIBHeader pHeader) {
super(pParent, pHeader);
mask = new BitmapIndexed(pParent, pHeader);
bitMask = new BitmapIndexed(pParent, pHeader);
}
boolean isTransparent(final int pX, final int pY) {
// NOTE: 1: Fully transparent, 0: Opaque...
return mask.bits[pX + pY * getWidth()] != 0;
return bitMask.bits[pX + pY * getWidth()] != 0;
}
public BufferedImage getImage() {
return mask.getImage();
return bitMask.getImage();
}
}
@@ -28,7 +28,9 @@
package com.twelvemonkeys.imageio.plugins.bmp;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
/**
* Describes an RGB/true color bitmap structure (16, 24 and 32 bits per pixel).
@@ -43,6 +45,38 @@ class BitmapRGB extends BitmapDescriptor {
}
public BufferedImage getImage() {
// Test is mask != null rather than hasMask(), as 32 bit (w/alpha)
// might still have bitmask, but we don't read or use it.
if (mask != null) {
image = createMaskedImage();
mask = null;
}
return image;
}
private BufferedImage createMaskedImage() {
BufferedImage masked = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D graphics = masked.createGraphics();
try {
graphics.drawImage(image, 0, 0, null);
}
finally {
graphics.dispose();
}
WritableRaster alphaRaster = masked.getAlphaRaster();
byte[] trans = {0x0};
for (int y = 0; y < getHeight(); y++) {
for (int x = 0; x < getWidth(); x++) {
if (mask.isTransparent(x, y)) {
alphaRaster.setDataElements(x, y, trans);
}
}
}
return masked;
}
}
@@ -66,14 +66,15 @@ import java.util.List;
// TODO: Decide whether DirectoryEntry or DIBHeader should be primary source for color count/bit count
// TODO: Support loading icons from DLLs, see
// <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwui/html/msdn_icons.asp">MSDN</a>
// Known issue: 256x256 PNG encoded icons does not have IndexColorModel even if stated in DirectoryEntry (seem impossible as the PNGs are all true color)
// Known issue: 256x256 PNG encoded icons does not have IndexColorModel even if stated in DirectoryEntry
// (seem impossible as the PNGs are all true color)
abstract class DIBImageReader extends ImageReaderBase {
// TODO: Consider moving the reading to inner classes (subclasses of BitmapDescriptor)
private Directory directory;
// TODO: Review these, make sure we don't have a memory leak
private Map<DirectoryEntry, DIBHeader> headers = new WeakHashMap<DirectoryEntry, DIBHeader>();
private Map<DirectoryEntry, BitmapDescriptor> descriptors = new WeakWeakMap<DirectoryEntry, BitmapDescriptor>();
private Map<DirectoryEntry, DIBHeader> headers = new WeakHashMap<>();
private Map<DirectoryEntry, BitmapDescriptor> descriptors = new WeakWeakMap<>();
private ImageReader pngImageReader;
@@ -101,7 +102,7 @@ abstract class DIBImageReader extends ImageReaderBase {
return getImageTypesPNG(entry);
}
List<ImageTypeSpecifier> types = new ArrayList<ImageTypeSpecifier>();
List<ImageTypeSpecifier> types = new ArrayList<>();
DIBHeader header = getHeader(entry);
// Use data from header to create specifier
@@ -121,10 +122,13 @@ abstract class DIBImageReader extends ImageReaderBase {
specifier = ImageTypeSpecifiers.createFromIndexColorModel(indexed.createColorModel());
break;
case 16:
// TODO: May have mask?!
specifier = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
break;
case 24:
specifier = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
specifier = new BitmapRGB(entry, header).hasMask()
? ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR)
: ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
break;
case 32:
specifier = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB);
@@ -184,6 +188,7 @@ abstract class DIBImageReader extends ImageReaderBase {
}
else {
Graphics2D g = destination.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.drawImage(image, 0, 0, null);
@@ -316,6 +321,8 @@ abstract class DIBImageReader extends ImageReaderBase {
descriptors.put(pEntry, descriptor);
}
System.err.println("descriptor: " + descriptor);
return descriptor.getImage();
}
@@ -335,7 +342,7 @@ abstract class DIBImageReader extends ImageReaderBase {
}
BitmapMask mask = new BitmapMask(pBitmap.entry, pBitmap.header);
readBitmapIndexed1(mask.mask, true);
readBitmapIndexed1(mask.bitMask, true);
pBitmap.setMask(mask);
}
@@ -370,7 +377,7 @@ abstract class DIBImageReader extends ImageReaderBase {
}
}
// NOTE: If we are reading the mask, we don't abort or progress
// NOTE: If we are reading the mask, we don't abort or report progress
if (!pAsMask) {
if (abortRequested()) {
processReadAborted();
@@ -455,7 +462,7 @@ abstract class DIBImageReader extends ImageReaderBase {
short[] pixels = new short[pBitmap.getWidth() * pBitmap.getHeight()];
// TODO: Support TYPE_USHORT_565 and the RGB 444/ARGB 4444 layouts
// Will create TYPE_USHORT_555;
// Will create TYPE_USHORT_555
DirectColorModel cm = new DirectColorModel(16, 0x7C00, 0x03E0, 0x001F);
DataBuffer buffer = new DataBufferShort(pixels, pixels.length);
WritableRaster raster = Raster.createPackedRaster(
@@ -480,6 +487,8 @@ abstract class DIBImageReader extends ImageReaderBase {
processImageProgress(100 * y / (float) pBitmap.getHeight());
}
// TODO: Might be mask!?
}
private void readBitmap24(final BitmapDescriptor pBitmap) throws IOException {
@@ -494,16 +503,19 @@ abstract class DIBImageReader extends ImageReaderBase {
cs, nBits, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE
);
int scanlineStride = pBitmap.getWidth() * 3;
// BMP rows are padded to 4 byte boundary
int rowSizeBytes = ((8 * scanlineStride + 31) / 32) * 4;
WritableRaster raster = Raster.createInterleavedRaster(
buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), 3, bOffs, null
buffer, pBitmap.getWidth(), pBitmap.getHeight(), scanlineStride, 3, bOffs, null
);
pBitmap.image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
for (int y = 0; y < pBitmap.getHeight(); y++) {
int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
imageInput.readFully(pixels, offset, pBitmap.getWidth() * 3);
// TODO: Possibly read padding byte here!
for (int y = 0; y < pBitmap.getHeight(); y++) {
int offset = (pBitmap.getHeight() - y - 1) * scanlineStride;
imageInput.readFully(pixels, offset, scanlineStride);
imageInput.skipBytes(rowSizeBytes - scanlineStride);
if (abortRequested()) {
processReadAborted();
@@ -512,6 +524,14 @@ abstract class DIBImageReader extends ImageReaderBase {
processImageProgress(100 * y / (float) pBitmap.getHeight());
}
// 24 bit icons usually have a bit mask
if (pBitmap.hasMask()) {
BitmapMask mask = new BitmapMask(pBitmap.entry, pBitmap.header);
readBitmapIndexed1(mask.bitMask, true);
pBitmap.setMask(mask);
}
}
private void readBitmap32(final BitmapDescriptor pBitmap) throws IOException {
@@ -535,6 +555,9 @@ abstract class DIBImageReader extends ImageReaderBase {
}
processImageProgress(100 * y / (float) pBitmap.getHeight());
}
// There might be a mask here as well, but we'll ignore it,
// and use the 8 bit alpha channel in the ARGB pixel data
}
private Directory getDirectory() throws IOException {
@@ -39,7 +39,9 @@ public class ICOImageReaderTest extends ImageReaderAbstractTest<ICOImageReader>
new Dimension(16, 16), new Dimension(16, 16), new Dimension(32, 32), new Dimension(32, 32),
new Dimension(48, 48), new Dimension(48, 48), new Dimension(256, 256), new Dimension(256, 256),
new Dimension(16, 16), new Dimension(32, 32), new Dimension(48, 48), new Dimension(256, 256)
)
),
// Problematic icon that reports 24 bit in the descriptor, but has separate 1 bit ''mask (height 2 x icon height)!
new TestData(getClassLoaderResource("/ico/rgb24bitmask.ico"), new Dimension(32, 32))
);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-clippath</artifactId>
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-core</artifactId>
<name>TwelveMonkeys :: ImageIO :: Core</name>
@@ -52,7 +52,7 @@ import java.util.Properties;
* <p />
* Color profiles may be configured by placing a property-file
* {@code com/twelvemonkeys/imageio/color/icc_profiles.properties}
* on the classpath, specifying the full path to the profile.
* on the classpath, specifying the full path to the profiles.
* ICC color profiles are probably already present on your system, or
* can be downloaded from
* <a href="http://www.color.org/profiles2.xalter">ICC</a>,
@@ -161,6 +161,11 @@ public final class ColorSpaces {
if (cs == null) {
cs = new ICC_ColorSpace(profile);
// Validate the color space, to avoid caching bad color spaces
// Will throw IllegalArgumentException or CMMException if the profile is bad
cs.fromRGB(new float[] {1f, 0f, 0f});
cache.put(key, cs);
}
@@ -195,7 +200,7 @@ public final class ColorSpaces {
* @return {@code true} if known to be offending, {@code false} otherwise
* @throws IllegalArgumentException if {@code profile} is {@code null}
*/
public static boolean isOffendingColorProfile(final ICC_Profile profile) {
static boolean isOffendingColorProfile(final ICC_Profile profile) {
Validate.notNull(profile, "profile");
// NOTE:
@@ -213,6 +218,26 @@ public final class ColorSpaces {
return data[ICC_Profile.icHdrRenderingIntent] != 0;
}
/**
* Tests whether an ICC color profile is valid.
* Invalid profiles are known to cause problems for {@link java.awt.image.ColorConvertOp}.
* <p />
* <em>
* Note that this method only tests if a color conversion using this profile is known to fail.
* There's no guarantee that the color conversion will succeed even if this method returns {@code false}.
* </em>
*
* @param profile the ICC color profile. May not be {@code null}.
* @return {@code profile} if valid.
* @throws IllegalArgumentException if {@code profile} is {@code null}
* @throws java.awt.color.CMMException if {@code profile} is invalid.
*/
public static ICC_Profile validateProfile(final ICC_Profile profile) {
createColorSpace(profile); // Creating a color space will fail if the profile is bad
return profile;
}
/**
* Returns the color space specified by the given color space constant.
* <p />
@@ -317,7 +342,7 @@ public final class ColorSpaces {
try {
return ICC_Profile.getInstance(profilePath);
}
catch (IOException ignore) {
catch (SecurityException | IOException ignore) {
if (DEBUG) {
ignore.printStackTrace();
}
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-icns</artifactId>
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-iff</artifactId>
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-jpeg</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
@@ -28,7 +28,6 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.color.ColorSpaces;
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
@@ -152,10 +151,10 @@ public class JPEGImageReader extends ImageReaderBase {
/** Cached list of JPEG segments we filter from the underlying stream */
private List<JPEGSegment> segments;
JPEGImageReader(final ImageReaderSpi provider, final ImageReader delegate) {
protected JPEGImageReader(final ImageReaderSpi provider, final ImageReader delegate) {
super(provider);
this.delegate = Validate.notNull(delegate);
this.delegate = Validate.notNull(delegate);
progressDelegator = new ProgressDelegator();
}
@@ -298,7 +297,9 @@ public class JPEGImageReader extends ImageReaderBase {
super.setInput(input, seekForwardOnly, ignoreMetadata);
// JPEGSegmentImageInputStream that filters out/skips bad/unnecessary segments
delegate.setInput(imageInput != null ? new JPEGSegmentImageInputStream(imageInput) : null, seekForwardOnly, ignoreMetadata);
delegate.setInput(imageInput != null
? new JPEGSegmentImageInputStream(imageInput)
: null, seekForwardOnly, ignoreMetadata);
}
@Override
@@ -408,6 +409,7 @@ public class JPEGImageReader extends ImageReaderBase {
if (DEBUG) {
System.err.println("Converting from " + intendedCS + " to " + (image.getColorModel().getColorSpace().isCS_sRGB() ? "sRGB" : image.getColorModel().getColorSpace()));
}
convert = new ColorConvertOp(intendedCS, image.getColorModel().getColorSpace(), null);
}
// Else, pass through with no conversion
@@ -469,12 +471,11 @@ public class JPEGImageReader extends ImageReaderBase {
YCbCrConverter.convertYCbCr2RGB(raster);
}
else if (csType == JPEGColorSpace.YCCK) {
YCbCrConverter.convertYCCK2CMYK(raster);
// TODO: Need to rethink this (non-) inversion, see #147
// TODO: Allow param to specify inversion, or possibly the PDF decode array
// flag0 bit 15, blend = 1 see http://graphicdesign.stackexchange.com/questions/12894/cmyk-jpegs-extracted-from-pdf-appear-inverted
if ((getAdobeDCT().flags0 & 0x8000) != 0) {
/// TODO: Better yet would be to not inverting in the first place, add flag to convertYCCK2CMYK
invertCMYK(raster);
}
boolean invert = true;// || (adobeDCT.flags0 & 0x8000) == 0;
YCbCrConverter.convertYCCK2CMYK(raster, invert);
}
else if (csType == JPEGColorSpace.CMYK) {
invertCMYK(raster);
@@ -629,7 +630,7 @@ public class JPEGImageReader extends ImageReaderBase {
}
}
private ICC_Profile ensureDisplayProfile(final ICC_Profile profile) {
protected ICC_Profile ensureDisplayProfile(final ICC_Profile profile) {
// NOTE: This is probably not the right way to do it... :-P
// TODO: Consider moving method to ColorSpaces class or new class in imageio.color package
@@ -835,7 +836,7 @@ public class JPEGImageReader extends ImageReaderBase {
return data;
}
ICC_Profile getEmbeddedICCProfile(final boolean allowBadIndexes) throws IOException {
protected ICC_Profile getEmbeddedICCProfile(final boolean allowBadIndexes) throws IOException {
// ICC v 1.42 (2006) annex B:
// APP2 marker (0xFFE2) + 2 byte length + ASCII 'ICC_PROFILE' + 0 (termination)
// + 1 byte chunk number + 1 byte chunk count (allows ICC profiles chunked in multiple APP2 segments)
@@ -857,7 +858,7 @@ public class JPEGImageReader extends ImageReaderBase {
return null;
}
return readICCProfileSafe(stream);
return readICCProfileSafe(stream, allowBadIndexes);
}
else if (!segments.isEmpty()) {
// NOTE: This is probably over-complicated, as I've never encountered ICC_PROFILE chunks out of order...
@@ -904,15 +905,17 @@ public class JPEGImageReader extends ImageReaderBase {
streams[badICC ? i : chunkNumber - 1] = stream;
}
return readICCProfileSafe(new SequenceInputStream(Collections.enumeration(Arrays.asList(streams))));
return readICCProfileSafe(new SequenceInputStream(Collections.enumeration(Arrays.asList(streams))), allowBadIndexes);
}
return null;
}
private ICC_Profile readICCProfileSafe(final InputStream stream) throws IOException {
private ICC_Profile readICCProfileSafe(final InputStream stream, final boolean allowBadProfile) throws IOException {
try {
return ICC_Profile.getInstance(stream);
ICC_Profile profile = ICC_Profile.getInstance(stream);
return allowBadProfile ? profile : ColorSpaces.validateProfile(profile);
}
catch (RuntimeException e) {
// NOTE: Throws either IllegalArgumentException or CMMException, depending on platform.
@@ -944,6 +947,11 @@ public class JPEGImageReader extends ImageReaderBase {
delegate.abort();
}
@Override
public ImageReadParam getDefaultReadParam() {
return delegate.getDefaultReadParam();
}
@Override
public boolean readerSupportsThumbnails() {
return true; // We support EXIF, JFIF and JFXX style thumbnails
@@ -1172,19 +1180,28 @@ public class JPEGImageReader extends ImageReaderBase {
rgb[offset + 2] = clamp(y + Cb_B_LUT[cb]);
}
static void convertYCCK2CMYK(final Raster raster) {
static void convertYCCK2CMYK(final Raster raster, final boolean invert) {
final int height = raster.getHeight();
final int width = raster.getWidth();
final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
convertYCCK2CMYK(data, data, (x + y * width) * 4);
if (invert) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
convertYCCK2CMYKInverted(data, data, (x + y * width) * 4);
}
}
}
else {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
convertYCCK2CMYK(data, data, (x + y * width) * 4);
}
}
}
}
private static void convertYCCK2CMYK(byte[] ycck, byte[] cmyk, int offset) {
private static void convertYCCK2CMYKInverted(byte[] ycck, byte[] cmyk, int offset) {
// Inverted
int y = 255 - ycck[offset ] & 0xff;
int cb = 255 - ycck[offset + 1] & 0xff;
@@ -1201,6 +1218,22 @@ public class JPEGImageReader extends ImageReaderBase {
cmyk[offset + 3] = (byte) k; // K passes through unchanged
}
private static void convertYCCK2CMYK(byte[] ycck, byte[] cmyk, int offset) {
int y = ycck[offset ] & 0xff;
int cb = ycck[offset + 1] & 0xff;
int cr = ycck[offset + 2] & 0xff;
int k = ycck[offset + 3] & 0xff;
int cmykC = MAXJSAMPLE - (y + Cr_R_LUT[cr]);
int cmykM = MAXJSAMPLE - (y + (Cb_G_LUT[cb] + Cr_G_LUT[cr] >> SCALEBITS));
int cmykY = MAXJSAMPLE - (y + Cb_B_LUT[cb]);
cmyk[offset ] = clamp(cmykC);
cmyk[offset + 1] = clamp(cmykM);
cmyk[offset + 2] = clamp(cmykY);
cmyk[offset + 3] = (byte) k; // K passes through unchanged
}
private static byte clamp(int val) {
return (byte) Math.max(0, Math.min(255, val));
}
@@ -1301,7 +1334,68 @@ public class JPEGImageReader extends ImageReaderBase {
}
public static void main(final String[] args) throws IOException {
for (final String arg : args) {
ImageIO.setUseCache(false);
int subX = 1;
int subY = 1;
int xOff = 0;
int yOff = 0;
Rectangle roi = null;
boolean metadata = false;
boolean thumbnails = false;
for (int argIdx = 0; argIdx < args.length; argIdx++) {
final String arg = args[argIdx];
if (arg.charAt(0) == '-') {
if (arg.equals("-s") || arg.equals("--subsample") && args.length > argIdx) {
String[] sub = args[++argIdx].split(",");
try {
if (sub.length >= 4) {
subX = Integer.parseInt(sub[0]);
subY = Integer.parseInt(sub[1]);
xOff = Integer.parseInt(sub[2]);
yOff = Integer.parseInt(sub[3]);
}
else {
subX = Integer.parseInt(sub[0]);
subY = sub.length > 1 ? Integer.parseInt(sub[1]) : subX;
}
}
catch (NumberFormatException e) {
System.err.println("Bad sub sampling (x,y): '" + args[argIdx] + "'");
}
}
else if (arg.equals("-r") || arg.equals("--roi") && args.length > argIdx) {
String[] region = args[++argIdx].split(",");
try {
if (region.length >= 4) {
roi = new Rectangle(Integer.parseInt(region[0]), Integer.parseInt(region[2]), Integer.parseInt(region[2]), Integer.parseInt(region[3]));
}
else {
roi = new Rectangle(Integer.parseInt(region[0]), Integer.parseInt(region[2]));
}
}
catch (IndexOutOfBoundsException | NumberFormatException e) {
System.err.println("Bad source region ([x,y,]w, h): '" + args[argIdx] + "'");
}
}
else if (arg.equals("-m") || arg.equals("--metadata")) {
metadata = true;
}
else if (arg.equals("-t") || arg.equals("--thumbnails")) {
thumbnails = true;
}
else {
System.err.println("Unknown argument: '" + arg + "'");
System.exit(-1);
}
continue;
}
File file = new File(arg);
ImageInputStream input = ImageIO.createImageInputStream(file);
@@ -1317,15 +1411,15 @@ public class JPEGImageReader extends ImageReaderBase {
continue;
}
ImageReader reader = readers.next();
// System.err.println("Reading using: " + reader);
final ImageReader reader = readers.next();
System.err.println("Reading using: " + reader);
reader.addIIOReadWarningListener(new IIOReadWarningListener() {
public void warningOccurred(ImageReader source, String warning) {
System.err.println("Warning: " + arg + ": " + warning);
}
});
reader.addIIOReadProgressListener(new ProgressListenerBase() {
final ProgressListenerBase listener = new ProgressListenerBase() {
private static final int MAX_W = 78;
int lastProgress = 0;
@@ -1354,29 +1448,35 @@ public class JPEGImageReader extends ImageReaderBase {
System.out.println("]");
}
});
};
reader.addIIOReadProgressListener(listener);
reader.setInput(input);
// For a tables-only image, we can't read image, but we should get metadata.
if (reader.getNumImages(true) == 0) {
IIOMetadata streamMetadata = reader.getStreamMetadata();
IIOMetadataNode streamNativeTree = (IIOMetadataNode) streamMetadata.getAsTree(streamMetadata.getNativeMetadataFormatName());
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(streamNativeTree, false);
continue;
}
try {
// For a tables-only image, we can't read image, but we should get metadata.
if (reader.getNumImages(true) == 0) {
IIOMetadata streamMetadata = reader.getStreamMetadata();
IIOMetadataNode streamNativeTree = (IIOMetadataNode) streamMetadata.getAsTree(streamMetadata.getNativeMetadataFormatName());
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(streamNativeTree, false);
continue;
}
BufferedImage image;
ImageReadParam param = reader.getDefaultReadParam();
// if (args.length > 1) {
// int sub = Integer.parseInt(args[1]);
// int sub = 4;
// param.setSourceSubsampling(sub, sub, 0, 0);
// }
BufferedImage image = reader.getImageTypes(0).next().createBufferedImage(reader.getWidth(0), reader.getHeight(0));
if (subX > 1 || subY > 1 || roi != null) {
param.setSourceSubsampling(subX, subY, xOff, yOff);
param.setSourceRegion(roi);
image = reader.getImageTypes(0).next().createBufferedImage((reader.getWidth(0) + subX - 1)/ subX, (reader.getHeight(0) + subY - 1) / subY);
}
else {
image = reader.getImageTypes(0).next().createBufferedImage(reader.getWidth(0), reader.getHeight(0));
}
param.setDestination(image);
// long start = System.currentTimeMillis();
long start = DEBUG ? System.currentTimeMillis() : 0;
try {
image = reader.read(0, param);
}
@@ -1387,12 +1487,13 @@ public class JPEGImageReader extends ImageReaderBase {
continue;
}
}
// System.err.println("Read time: " + (System.currentTimeMillis() - start) + " ms");
// System.err.println("image: " + image);
if (DEBUG) {
System.err.println("Read time: " + (System.currentTimeMillis() - start) + " ms");
System.err.println("image: " + image);
}
// image = new ResampleOp(reader.getWidth(0) / 4, reader.getHeight(0) / 4, ResampleOp.FILTER_LANCZOS).filter(image, null);
/*
int maxW = 1280;
int maxH = 800;
if (image.getWidth() > maxW || image.getHeight() > maxH) {
@@ -1406,34 +1507,45 @@ public class JPEGImageReader extends ImageReaderBase {
}
// System.err.println("Scale time: " + (System.currentTimeMillis() - start) + " ms");
}
*/
showIt(image, String.format("Image: %s [%d x %d]", file.getName(), reader.getWidth(0), reader.getHeight(0)));
try {
IIOMetadata imageMetadata = reader.getImageMetadata(0);
System.out.println("Metadata for File: " + file.getName());
if (metadata) {
try {
IIOMetadata imageMetadata = reader.getImageMetadata(0);
System.out.println("Metadata for File: " + file.getName());
if (imageMetadata.getNativeMetadataFormatName() != null) {
System.out.println("Native:");
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(imageMetadata.getAsTree(imageMetadata.getNativeMetadataFormatName()), false);
if (imageMetadata.getNativeMetadataFormatName() != null) {
System.out.println("Native:");
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(imageMetadata.getAsTree(imageMetadata.getNativeMetadataFormatName()), false);
}
if (imageMetadata.isStandardMetadataFormatSupported()) {
System.out.println("Standard:");
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(imageMetadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName), false);
}
System.out.println();
}
if (imageMetadata.isStandardMetadataFormatSupported()) {
System.out.println("Standard:");
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(imageMetadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName), false);
}
System.out.println();
int numThumbnails = reader.getNumThumbnails(0);
for (int i = 0; i < numThumbnails; i++) {
BufferedImage thumbnail = reader.readThumbnail(0, i);
// System.err.println("thumbnail: " + thumbnail);
showIt(thumbnail, String.format("Thumbnail: %s [%d x %d]", file.getName(), thumbnail.getWidth(), thumbnail.getHeight()));
catch (IIOException e) {
System.err.println("Could not read thumbnails: " + arg + ": " + e.getMessage());
e.printStackTrace();
}
}
catch (IIOException e) {
System.err.println("Could not read thumbnails: " + arg + ": " + e.getMessage());
e.printStackTrace();
if (thumbnails) {
try {
int numThumbnails = reader.getNumThumbnails(0);
for (int i = 0; i < numThumbnails; i++) {
BufferedImage thumbnail = reader.readThumbnail(0, i);
// System.err.println("thumbnail: " + thumbnail);
showIt(thumbnail, String.format("Thumbnail: %s [%d x %d]", file.getName(), thumbnail.getWidth(), thumbnail.getHeight()));
}
}
catch (IIOException e) {
System.err.println("Could not read thumbnails: " + arg + ": " + e.getMessage());
e.printStackTrace();
}
}
}
catch (Throwable t) {
@@ -29,6 +29,7 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.lang.Validate;
@@ -48,14 +49,14 @@ import java.util.Locale;
* @version $Id: JPEGImageReaderSpi.java,v 1.0 24.01.11 22.12 haraldk Exp$
*/
public class JPEGImageReaderSpi extends ImageReaderSpiBase {
private ImageReaderSpi delegateProvider;
protected ImageReaderSpi delegateProvider;
/**
* Constructor for use by {@link javax.imageio.spi.IIORegistry} only.
* The instance created will not work without being properly registered.
*/
public JPEGImageReaderSpi() {
super(new JPEGProviderInfo());
this(new JPEGProviderInfo());
}
/**
@@ -69,6 +70,15 @@ public class JPEGImageReaderSpi extends ImageReaderSpiBase {
this.delegateProvider = Validate.notNull(delegateProvider);
}
/**
* Constructor for subclasses.
*
* @param info
*/
protected JPEGImageReaderSpi(final ReaderWriterProviderInfo info) {
super(info);
}
static ImageReaderSpi lookupDelegateProvider(final ServiceRegistry registry) {
Iterator<ImageReaderSpi> providers = registry.getServiceProviders(ImageReaderSpi.class, true);
@@ -83,7 +93,7 @@ public class JPEGImageReaderSpi extends ImageReaderSpiBase {
return null;
}
@SuppressWarnings({"unchecked"})
@SuppressWarnings({"unchecked", "deprecation"})
@Override
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
if (delegateProvider == null) {
@@ -63,8 +63,7 @@ import static org.junit.Assume.assumeNoException;
import static org.junit.Assume.assumeNotNull;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.*;
/**
* JPEGImageReaderTest
@@ -75,9 +74,9 @@ import static org.mockito.Mockito.verify;
*/
public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader> {
private static final JPEGImageReaderSpi SPI = new JPEGImageReaderSpi(lookupDelegateProvider());
protected static final JPEGImageReaderSpi SPI = new JPEGImageReaderSpi(lookupDelegateProvider());
private static ImageReaderSpi lookupDelegateProvider() {
protected static ImageReaderSpi lookupDelegateProvider() {
return JPEGImageReaderSpi.lookupDelegateProvider(IIORegistry.getDefaultInstance());
}
@@ -87,6 +86,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
return Arrays.asList(
new TestData(getClassLoaderResource("/jpeg/cmm-exception-adobe-rgb.jpg"), new Dimension(626, 76)),
new TestData(getClassLoaderResource("/jpeg/cmm-exception-srgb.jpg"), new Dimension(1800, 1200)),
new TestData(getClassLoaderResource("/jpeg/corrupted-icc-srgb.jpg"), new Dimension(1024, 685)),
new TestData(getClassLoaderResource("/jpeg/gray-sample.jpg"), new Dimension(386, 396)),
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)),
@@ -370,7 +370,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
assertEquals(1772, image.getWidth());
assertEquals(8, image.getHeight());
verify(warningListener).warningOccurred(eq(reader), anyString());
verify(warningListener, atLeast(1)).warningOccurred(eq(reader), anyString());
}
@Test
Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>imageio-metadata</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-pcx</artifactId>
<name>TwelveMonkeys :: ImageIO :: PCX plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-pdf</artifactId>
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-pict</artifactId>
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-pnm</artifactId>
<name>TwelveMonkeys :: ImageIO :: PNM plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-psd</artifactId>
<name>TwelveMonkeys :: ImageIO :: PSD plugin</name>
@@ -74,7 +74,7 @@ final class PSDLayerInfo {
long extraDataSize = pInput.readUnsignedInt();
// Layer mask/adjustment layer data
int layerMaskDataSize = pInput.readInt(); // May be 0, 20 or 36 bytes...
int layerMaskDataSize = pInput.readInt(); // May be 0, 20 or variable (up to 55) bytes...
if (layerMaskDataSize != 0) {
layerMaskData = new PSDLayerMaskData(pInput, layerMaskDataSize);
}
@@ -47,42 +47,90 @@ final class PSDLayerMaskData {
private int defaultColor;
private int flags;
private boolean large;
private int realFlags;
private int realUserBackground;
private int realTop;
private int realLeft;
private int realBottom;
private int realRight;
private int maskParams;
private int userMaskDensity;
private double userMaskFeather;
private int vectorMaskDensity;
private double vectorMaskFeather;
PSDLayerMaskData(final ImageInputStream pInput, final int pSize) throws IOException {
if (pSize != 20 && pSize != 36) {
throw new IIOException("Illegal PSD Layer Mask data size: " + pSize + " (expected 20 or 36)");
if (pSize < 20 || pSize > 55) {
throw new IIOException("Illegal PSD Layer Mask data size: " + pSize + " (expected between 20 and 55)");
}
// Rectangle enclosing layer mask: Top, left, bottom, right.
top = pInput.readInt();
left = pInput.readInt();
bottom = pInput.readInt();
right = pInput.readInt();
// Default color. 0 or 255
defaultColor = pInput.readUnsignedByte();
// Flags.
// bit 0 = position relative to layer
// bit 1 = layer mask disabled
// bit 2 = invert layer mask when blending (Obsolete)
// bit 3 = indicates that the user mask actually came from rendering other data
// bit 4 = indicates that the user and/or vector masks have parameters applied to them
flags = pInput.readUnsignedByte();
if (pSize == 20) {
int dataLeft = pSize - 18;
if ((flags & 0x10) != 0) {
// Mask Parameters. Only present if bit 4 of Flags set above.
maskParams = pInput.readUnsignedByte();
dataLeft--;
// Mask Parameters bit flags present as follows:
// bit 0 = user mask density, 1 byte
// bit 1 = user mask feather, 8 byte, double
// bit 2 = vector mask density, 1 byte
// bit 3 = vector mask feather, 8 bytes, double
if ((maskParams & 0x01) != 0) {
userMaskDensity = pInput.readByte();
dataLeft--;
}
if ((maskParams & 0x02) != 0) {
userMaskFeather = pInput.readDouble();
dataLeft -= 8;
}
if ((maskParams & 0x04) != 0) {
vectorMaskDensity = pInput.readByte();
dataLeft--;
}
if ((maskParams & 0x08) != 0) {
vectorMaskFeather = pInput.readDouble();
dataLeft -= 8;
}
}
// Padding. Only present if size = 20. Otherwise the following is present
if (pSize == 20 && dataLeft == 2) {
pInput.readShort(); // Pad
dataLeft -= 2;
}
else {
// TODO: What to make out of this?
large = true;
if (dataLeft >= 2) {
// Real Flags. Same as Flags information above.
flags = pInput.readUnsignedByte();
dataLeft--;
// Real user mask background. 0 or 255.
defaultColor = pInput.readUnsignedByte();
dataLeft--;
}
if (dataLeft >= 16) {
// Rectangle enclosing layer mask: Top, left, bottom, right.
top = pInput.readInt();
left = pInput.readInt();
bottom = pInput.readInt();
right = pInput.readInt();
dataLeft -= 16;
}
}
realFlags = pInput.readUnsignedByte();
realUserBackground = pInput.readUnsignedByte();
realTop = pInput.readInt();
realLeft = pInput.readInt();
realBottom = pInput.readInt();
realRight = pInput.readInt();
if (dataLeft > 0) {
pInput.skipBytes(dataLeft);
}
}
@@ -97,40 +145,54 @@ final class PSDLayerMaskData {
builder.append(", default color: ").append(defaultColor);
builder.append(", flags: ").append(Integer.toBinaryString(flags));
// TODO: Maybe the flag bits have oposite order?
builder.append(" (");
if ((flags & 0x01) != 0) {
builder.append("Pos. rel. to layer");
builder.append("relative");
}
else {
builder.append("Pos. abs.");
builder.append("absolute");
}
if ((flags & 0x02) != 0) {
builder.append(", Mask disabled");
builder.append(", disabled");
}
else {
builder.append(", Mask enabled");
builder.append(", enabled");
}
if ((flags & 0x04) != 0) {
builder.append(", Invert mask");
builder.append(", inverted");
}
if ((flags & 0x08) != 0) {
builder.append(", Unknown bit 3");
builder.append(", from rendered data");
}
if ((flags & 0x10) != 0) {
builder.append(", Unknown bit 4");
builder.append(", has parameters");
}
if ((flags & 0x20) != 0) {
builder.append(", Unknown bit 5");
builder.append(", unknown flag (bit 5)");
}
if ((flags & 0x40) != 0) {
builder.append(", Unknown bit 6");
builder.append(", unknown flag (bit 6)");
}
if ((flags & 0x80) != 0) {
builder.append(", Unknown bit 7");
builder.append(", unknown flag (bit 7)");
}
builder.append(")");
if ((flags & 0x10) != 0) {
if ((maskParams & 0x01) != 0) {
builder.append(", userMaskDensity: ").append(userMaskDensity);
}
if ((maskParams & 0x02) != 0) {
builder.append(", userMaskFeather: ").append(userMaskFeather);
}
if ((maskParams & 0x04) != 0) {
builder.append(", vectorMaskDensity: ").append(vectorMaskDensity);
}
if ((maskParams & 0x08) != 0) {
builder.append(", vectorMaskFeather: ").append(vectorMaskFeather);
}
}
builder.append("]");
return builder.toString();
}
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-reference</artifactId>
<name>TwelveMonkeys :: ImageIO :: reference test cases</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-sgi</artifactId>
<name>TwelveMonkeys :: ImageIO :: SGI plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-tga</artifactId>
<name>TwelveMonkeys :: ImageIO :: TGA plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-thumbsdb</artifactId>
<name>TwelveMonkeys :: ImageIO :: Thumbs.db plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<artifactId>imageio-tiff</artifactId>
<name>TwelveMonkeys :: ImageIO :: TIFF plugin</name>
@@ -36,7 +36,7 @@ import java.io.IOException;
import java.io.InputStream;
/**
* CCITT Modified Huffman RLE, Group 3 (T4) and Group 4 (T6) fax compression.
* CCITT Modified Huffman RLE<!--, and hopefully soon: Group 3 (T4) and Group 4 (T6) fax compression-->.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
@@ -145,9 +145,9 @@ public final class TIFFImageWriter extends ImageWriterBase {
entries.add(new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, renderedImage.getHeight()));
// entries.add(new TIFFEntry(TIFF.TAG_ORIENTATION, 1)); // (optional)
entries.add(new TIFFEntry(TIFF.TAG_BITS_PER_SAMPLE, asShortArray(sampleModel.getSampleSize())));
// If numComponents > 3, write ExtraSamples
if (numComponents > 3) {
// TODO: Write per component > 3
// If numComponents > numColorComponents, write ExtraSamples
if (numComponents > colorModel.getNumColorComponents()) {
// TODO: Write per component > numColorComponents
if (colorModel.hasAlpha()) {
entries.add(new TIFFEntry(TIFF.TAG_EXTRA_SAMPLES, colorModel.isAlphaPremultiplied() ? TIFFBaseline.EXTRASAMPLE_ASSOCIATED_ALPHA : TIFFBaseline.EXTRASAMPLE_UNASSOCIATED_ALPHA));
}
@@ -169,6 +169,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
default:
}
// TODO: We might want to support CMYK in JPEG as well...
int photometric = compression == TIFFExtension.COMPRESSION_JPEG ?
TIFFExtension.PHOTOMETRIC_YCBCR :
getPhotometricInterpretation(colorModel);
@@ -684,7 +685,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
// BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), BufferedImage.TYPE_INT_BGR);
// BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
BufferedImage image;
if (type < 0 || type == original.getType()) {
if (type <= 0 || type == original.getType()) {
image = original;
}
else if (type == BufferedImage.TYPE_BYTE_INDEXED) {
@@ -0,0 +1 @@
com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriterSpi
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.twelvemonkeys.imageio</groupId>
+1 -1
View File
@@ -8,7 +8,7 @@
</parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Twelvemonkeys</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.1.0</version>
<version>3.1.3-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>