mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 03:55:28 -04:00
PICT metadata + PNTG support
This commit is contained in:
parent
bb650e5280
commit
967f8e6984
@ -33,7 +33,8 @@ The goal is to create a set of efficient and robust ImageIO plug-ins, that can b
|
|||||||
| | JPEG Lossless | | ✔ | - | Native & Standard |
|
| | JPEG Lossless | | ✔ | - | Native & Standard |
|
||||||
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | ✔ | - | Standard |
|
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | ✔ | - | Standard |
|
||||||
| | DCX | Multi-page PCX fax document | ✔ | - | Standard |
|
| | DCX | Multi-page PCX fax document | ✔ | - | Standard |
|
||||||
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple Mac Paint Picture Format | ✔ | ✔ | - |
|
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple QuickTime Picture Format | ✔ | ✔ | Standard |
|
||||||
|
| | PNTG | Apple MacPaint Picture Format | ✔ | | Standard |
|
||||||
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | ✔ | ✔ | Standard |
|
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | ✔ | ✔ | Standard |
|
||||||
| | PBM | NetPBM Portable Bit Map | ✔ | - | Standard |
|
| | PBM | NetPBM Portable Bit Map | ✔ | - | Standard |
|
||||||
| | PGM | NetPBM Portable Grey Map | ✔ | - | Standard |
|
| | PGM | NetPBM Portable Grey Map | ✔ | - | Standard |
|
||||||
|
@ -265,8 +265,9 @@ public abstract class ImageReaderBase extends ImageReader {
|
|||||||
// - transferType is ok
|
// - transferType is ok
|
||||||
// - bands are ok
|
// - bands are ok
|
||||||
// TODO: Test if color model is ok?
|
// TODO: Test if color model is ok?
|
||||||
if (specifier.getSampleModel().getTransferType() == dest.getSampleModel().getTransferType() &&
|
if (specifier.getSampleModel().getTransferType() == dest.getSampleModel().getTransferType()
|
||||||
specifier.getNumBands() <= dest.getSampleModel().getNumBands()) {
|
&& Arrays.equals(specifier.getSampleModel().getSampleSize(), dest.getSampleModel().getSampleSize())
|
||||||
|
&& specifier.getNumBands() <= dest.getSampleModel().getNumBands()) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@ import com.twelvemonkeys.io.enc.DecoderStream;
|
|||||||
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
||||||
|
|
||||||
import javax.imageio.*;
|
import javax.imageio.*;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
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.*;
|
||||||
@ -76,11 +77,8 @@ import java.awt.geom.AffineTransform;
|
|||||||
import java.awt.geom.Area;
|
import java.awt.geom.Area;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reader for Apple Mac Paint Picture (PICT) format.
|
* Reader for Apple Mac Paint Picture (PICT) format.
|
||||||
@ -123,10 +121,11 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
private double screenImageYRatio;
|
private double screenImageYRatio;
|
||||||
|
|
||||||
// List of images created during image import
|
// List of images created during image import
|
||||||
private List<BufferedImage> images = new ArrayList<>();
|
private final List<BufferedImage> images = new ArrayList<>();
|
||||||
private long imageStartStreamPos;
|
private long imageStartStreamPos;
|
||||||
protected int picSize;
|
protected int picSize;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public PICTImageReader() {
|
public PICTImageReader() {
|
||||||
this(null);
|
this(null);
|
||||||
}
|
}
|
||||||
@ -168,14 +167,14 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
* @throws IOException if an I/O error occurs while reading the image.
|
* @throws IOException if an I/O error occurs while reading the image.
|
||||||
*/
|
*/
|
||||||
private void readPICTHeader(final ImageInputStream pStream) throws IOException {
|
private void readPICTHeader(final ImageInputStream pStream) throws IOException {
|
||||||
pStream.seek(0l);
|
pStream.seek(0L);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
readPICTHeader0(pStream);
|
readPICTHeader0(pStream);
|
||||||
}
|
}
|
||||||
catch (IIOException e) {
|
catch (IIOException e) {
|
||||||
// Rest and try again
|
// Rest and try again
|
||||||
pStream.seek(0l);
|
pStream.seek(0L);
|
||||||
|
|
||||||
// Skip first 512 bytes
|
// Skip first 512 bytes
|
||||||
PICTImageReaderSpi.skipNullHeader(pStream);
|
PICTImageReaderSpi.skipNullHeader(pStream);
|
||||||
@ -207,7 +206,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
System.out.println("frame: " + frame);
|
System.out.println("frame: " + frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set default display ratios. 72 dpi is the standard Macintosh resolution.
|
// Set default display ratios. 72 dpi is the standard Mac resolution.
|
||||||
screenImageXRatio = 1.0;
|
screenImageXRatio = 1.0;
|
||||||
screenImageYRatio = 1.0;
|
screenImageYRatio = 1.0;
|
||||||
|
|
||||||
@ -215,7 +214,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
boolean isExtendedV2 = false;
|
boolean isExtendedV2 = false;
|
||||||
int version = pStream.readShort();
|
int version = pStream.readShort();
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("PICT version: 0x%04x", version));
|
System.out.printf("PICT version: 0x%04x%n", version);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version == (PICT.OP_VERSION << 8) + 0x01) {
|
if (version == (PICT.OP_VERSION << 8) + 0x01) {
|
||||||
@ -231,24 +230,20 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
int headerVersion = pStream.readInt();
|
int headerVersion = pStream.readInt();
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("headerVersion: 0x%04x", headerVersion));
|
System.out.printf("headerVersion: 0x%04x%n", headerVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This (headerVersion) should be picture size (bytes) for non-V2-EXT...?
|
// TODO: This (headerVersion) should be picture size (bytes) for non-V2-EXT...?
|
||||||
// - but.. We should take care to make sure we don't mis-interpret non-PICT data...
|
// - but.. We should take care to make sure we don't mis-interpret non-PICT data...
|
||||||
//if (headerVersion == PICT.HEADER_V2) {
|
|
||||||
if ((headerVersion & 0xffff0000) != PICT.HEADER_V2_EXT) {
|
if ((headerVersion & 0xffff0000) != PICT.HEADER_V2_EXT) {
|
||||||
// TODO: Test this.. Looks dodgy to me..
|
// TODO: Test this.. Looks dodgy to me..
|
||||||
// Get the image resolution and calculate the ratio between
|
// Get the image resolution and calculate the ratio between
|
||||||
// the default Mac screen resolution and the image resolution
|
// the default Mac screen resolution and the image resolution
|
||||||
|
|
||||||
// int y (fixed point)
|
// int y, x, w(?), h (fixed point)
|
||||||
double y2 = PICTUtil.readFixedPoint(pStream);
|
double y2 = PICTUtil.readFixedPoint(pStream);
|
||||||
// int x (fixed point)
|
|
||||||
double x2 = PICTUtil.readFixedPoint(pStream);
|
double x2 = PICTUtil.readFixedPoint(pStream);
|
||||||
// int w (fixed point)
|
|
||||||
double w2 = PICTUtil.readFixedPoint(pStream); // ?!
|
double w2 = PICTUtil.readFixedPoint(pStream); // ?!
|
||||||
// int h (fixed point)
|
|
||||||
double h2 = PICTUtil.readFixedPoint(pStream);
|
double h2 = PICTUtil.readFixedPoint(pStream);
|
||||||
|
|
||||||
screenImageXRatio = (w - x) / (w2 - x2);
|
screenImageXRatio = (w - x) / (w2 - x2);
|
||||||
@ -264,7 +259,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
// int reserved
|
// int reserved
|
||||||
pStream.skipBytes(4);
|
pStream.skipBytes(4);
|
||||||
}
|
}
|
||||||
else /*if ((headerVersion & 0xffff0000) == PICT.HEADER_V2_EXT)*/ {
|
else {
|
||||||
isExtendedV2 = true;
|
isExtendedV2 = true;
|
||||||
// Get the image resolution
|
// Get the image resolution
|
||||||
// Not sure if they are useful for anything...
|
// Not sure if they are useful for anything...
|
||||||
@ -281,13 +276,10 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
// Get the image resolution and calculate the ratio between
|
// Get the image resolution and calculate the ratio between
|
||||||
// the default Mac screen resolution and the image resolution
|
// the default Mac screen resolution and the image resolution
|
||||||
// short y
|
// short y, x, h, w
|
||||||
short y2 = pStream.readShort();
|
short y2 = pStream.readShort();
|
||||||
// short x
|
|
||||||
short x2 = pStream.readShort();
|
short x2 = pStream.readShort();
|
||||||
// short h
|
|
||||||
short h2 = pStream.readShort();
|
short h2 = pStream.readShort();
|
||||||
// short w
|
|
||||||
short w2 = pStream.readShort();
|
short w2 = pStream.readShort();
|
||||||
|
|
||||||
screenImageXRatio = (w - x) / (double) (w2 - x2);
|
screenImageXRatio = (w - x) / (double) (w2 - x2);
|
||||||
@ -400,7 +392,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PICT.OP_CLIP_RGN:// OK for RECTS, not for regions yet
|
case PICT.OP_CLIP_RGN:// OK for RECTs, not for regions yet
|
||||||
// Read the region
|
// Read the region
|
||||||
if ((region = readRegion(pStream, bounds)) == null) {
|
if ((region = readRegion(pStream, bounds)) == null) {
|
||||||
throw new IIOException("Could not read region");
|
throw new IIOException("Could not read region");
|
||||||
@ -735,12 +727,13 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
case 0x25:
|
case 0x25:
|
||||||
case 0x26:
|
case 0x26:
|
||||||
case 0x27:
|
case 0x27:
|
||||||
|
case 0x2F:
|
||||||
// Apple reserved
|
// Apple reserved
|
||||||
dataLength = pStream.readUnsignedShort();
|
dataLength = pStream.readUnsignedShort();
|
||||||
|
|
||||||
pStream.readFully(new byte[dataLength], 0, dataLength);
|
pStream.readFully(new byte[dataLength], 0, dataLength);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode));
|
System.out.printf("%s: 0x%04x%n", PICT.APPLE_USE_RESERVED_FIELD, opCode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -829,14 +822,6 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x2F:
|
|
||||||
dataLength = pStream.readUnsignedShort();
|
|
||||||
pStream.readFully(new byte[dataLength], 0, dataLength);
|
|
||||||
if (DEBUG) {
|
|
||||||
System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
// Rect treatments
|
// Rect treatments
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
@ -920,7 +905,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
case 0x003e:
|
case 0x003e:
|
||||||
case 0x003f:
|
case 0x003f:
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode));
|
System.out.printf("%s: 0x%04x%n", PICT.APPLE_USE_RESERVED_FIELD, opCode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1092,7 +1077,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
case 0x57:
|
case 0x57:
|
||||||
pStream.readFully(new byte[8], 0, 8);
|
pStream.readFully(new byte[8], 0, 8);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode));
|
System.out.printf("%s: 0x%04x%n", PICT.APPLE_USE_RESERVED_FIELD, opCode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1187,7 +1172,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
case 0x67:
|
case 0x67:
|
||||||
pStream.readFully(new byte[12], 0, 12);
|
pStream.readFully(new byte[12], 0, 12);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode));
|
System.out.printf("%s: 0x%04x%n", PICT.APPLE_USE_RESERVED_FIELD, opCode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x6d:
|
case 0x6d:
|
||||||
@ -1195,7 +1180,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
case 0x6f:
|
case 0x6f:
|
||||||
pStream.readFully(new byte[4], 0, 4);
|
pStream.readFully(new byte[4], 0, 4);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode));
|
System.out.printf("%s: 0x%04x%n", PICT.APPLE_USE_RESERVED_FIELD, opCode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1283,7 +1268,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
case 0x7e:
|
case 0x7e:
|
||||||
case 0x7f:
|
case 0x7f:
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode));
|
System.out.printf("%s: 0x%04x%n", PICT.APPLE_USE_RESERVED_FIELD, opCode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1293,7 +1278,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
// Read the polygon
|
// Read the polygon
|
||||||
polygon = readPoly(pStream, bounds);
|
polygon = readPoly(pStream, bounds);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode));
|
System.out.printf("%s: 0x%04x%n", PICT.APPLE_USE_RESERVED_FIELD, opCode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1384,7 +1369,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
// Read the region
|
// Read the region
|
||||||
region = readRegion(pStream, bounds);
|
region = readRegion(pStream, bounds);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode));
|
System.out.printf("%s: 0x%04x%n", PICT.APPLE_USE_RESERVED_FIELD, opCode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1414,7 +1399,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
dataLength = pStream.readUnsignedShort();
|
dataLength = pStream.readUnsignedShort();
|
||||||
pStream.readFully(new byte[dataLength], 0, dataLength);
|
pStream.readFully(new byte[dataLength], 0, dataLength);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("%s: 0x%04x - length: %d", PICT.APPLE_USE_RESERVED_FIELD, opCode, dataLength));
|
System.out.printf("%s: 0x%04x - length: %d%n", PICT.APPLE_USE_RESERVED_FIELD, opCode, dataLength);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1442,7 +1427,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
dataLength = pStream.readUnsignedShort();
|
dataLength = pStream.readUnsignedShort();
|
||||||
pStream.readFully(new byte[dataLength], 0, dataLength);
|
pStream.readFully(new byte[dataLength], 0, dataLength);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode));
|
System.out.printf("%s: 0x%04x%n", PICT.APPLE_USE_RESERVED_FIELD, opCode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1478,7 +1463,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
// TODO: Read this as well, need test data
|
// TODO: Read this as well, need test data
|
||||||
dataLength = pStream.readInt();
|
dataLength = pStream.readInt();
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("unCompressedQuickTime, length %d", dataLength));
|
System.out.printf("unCompressedQuickTime, length %d%n", dataLength);
|
||||||
}
|
}
|
||||||
pStream.readFully(new byte[dataLength], 0, dataLength);
|
pStream.readFully(new byte[dataLength], 0, dataLength);
|
||||||
break;
|
break;
|
||||||
@ -1515,7 +1500,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("%s: 0x%04x - length: %s", PICT.APPLE_USE_RESERVED_FIELD, opCode, dataLength));
|
System.out.printf("%s: 0x%04x - length: %s%n", PICT.APPLE_USE_RESERVED_FIELD, opCode, dataLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataLength != 0) {
|
if (dataLength != 0) {
|
||||||
@ -1577,7 +1562,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
matrix[i] = pStream.readInt();
|
matrix[i] = pStream.readInt();
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(String.format("matrix: %s", Arrays.toString(matrix)));
|
System.out.printf("matrix: %s%n", Arrays.toString(matrix));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matte
|
// Matte
|
||||||
@ -1833,7 +1818,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
// TODO: This works for single image PICTs only...
|
// TODO: This works for single image PICTs only...
|
||||||
// However, this is the most common case. Ok for now
|
// However, this is the most common case. Ok for now
|
||||||
processImageProgress(scanline * 100 / srcRect.height);
|
processImageProgress(scanline * 100 / (float) srcRect.height);
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
processReadAborted();
|
processReadAborted();
|
||||||
|
|
||||||
@ -2134,7 +2119,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
// TODO: This works for single image PICTs only...
|
// TODO: This works for single image PICTs only...
|
||||||
// However, this is the most common case. Ok for now
|
// However, this is the most common case. Ok for now
|
||||||
processImageProgress(scanline * 100 / srcRect.height);
|
processImageProgress(scanline * 100 / (float) srcRect.height);
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
processReadAborted();
|
processReadAborted();
|
||||||
|
|
||||||
@ -2626,7 +2611,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
return getYPtCoord(getPICTFrame().height);
|
return getYPtCoord(getPICTFrame().height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterator<ImageTypeSpecifier> getImageTypes(int pIndex) throws IOException {
|
public Iterator<ImageTypeSpecifier> getImageTypes(int pIndex) {
|
||||||
// TODO: The images look slightly different in Preview.. Could indicate the color space is wrong...
|
// TODO: The images look slightly different in Preview.. Could indicate the color space is wrong...
|
||||||
return Collections.singletonList(
|
return Collections.singletonList(
|
||||||
ImageTypeSpecifiers.createPacked(
|
ImageTypeSpecifiers.createPacked(
|
||||||
@ -2636,11 +2621,19 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
).iterator();
|
).iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||||
|
checkBounds(imageIndex);
|
||||||
|
getPICTFrame(); // TODO: Would probably be better to use readPictHeader here, but it isn't cached
|
||||||
|
|
||||||
|
return new PICTMetadata(version, screenImageXRatio, screenImageYRatio);
|
||||||
|
}
|
||||||
|
|
||||||
protected static void showIt(final BufferedImage pImage, final String pTitle) {
|
protected static void showIt(final BufferedImage pImage, final String pTitle) {
|
||||||
ImageReaderBase.showIt(pImage, pTitle);
|
ImageReaderBase.showIt(pImage, pTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(final String[] pArgs) throws IOException {
|
public static void main(final String[] pArgs) {
|
||||||
ImageReader reader = new PICTImageReader(new PICTImageReaderSpi());
|
ImageReader reader = new PICTImageReader(new PICTImageReaderSpi());
|
||||||
|
|
||||||
for (String arg : pArgs) {
|
for (String arg : pArgs) {
|
||||||
|
@ -71,7 +71,7 @@ public final class PICTImageReaderSpi extends ImageReaderSpiBase {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (isPICT(stream)) {
|
if (isPICT(stream)) {
|
||||||
// If PICT Clipping format, return true immediately
|
// If PICT clipboard format, return true immediately
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -154,8 +154,8 @@ public final class PICTImageReaderSpi extends ImageReaderSpiBase {
|
|||||||
pStream.skipBytes(PICT.PICT_NULL_HEADER_SIZE);
|
pStream.skipBytes(PICT.PICT_NULL_HEADER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: As the PICT format has a very weak identifier, a true return value is not necessarily a PICT...
|
||||||
private boolean isPICT(final ImageInputStream pStream) throws IOException {
|
private boolean isPICT(final ImageInputStream pStream) throws IOException {
|
||||||
// TODO: Need to validate better...
|
|
||||||
// Size may be 0, so we can't use this for validation...
|
// Size may be 0, so we can't use this for validation...
|
||||||
pStream.readUnsignedShort();
|
pStream.readUnsignedShort();
|
||||||
|
|
||||||
@ -169,8 +169,8 @@ public final class PICTImageReaderSpi extends ImageReaderSpiBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate magic
|
||||||
int magic = pStream.readInt();
|
int magic = pStream.readInt();
|
||||||
|
|
||||||
return (magic & 0xffff0000) == PICT.MAGIC_V1 || magic == PICT.MAGIC_V2;
|
return (magic & 0xffff0000) == PICT.MAGIC_V1 || magic == PICT.MAGIC_V2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,6 +179,6 @@ public final class PICTImageReaderSpi extends ImageReaderSpiBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getDescription(final Locale pLocale) {
|
public String getDescription(final Locale pLocale) {
|
||||||
return "Apple Mac Paint Picture (PICT) image reader";
|
return "Apple MacPaint/QuickDraw Picture (PICT) image reader";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
package com.twelvemonkeys.imageio.plugins.pict;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||||
|
|
||||||
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PICTMetadata.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: PICTMetadata.java,v 1.0 23/03/2021 haraldk Exp$
|
||||||
|
*/
|
||||||
|
public class PICTMetadata extends AbstractMetadata {
|
||||||
|
|
||||||
|
private final int version;
|
||||||
|
private final double screenImageXRatio;
|
||||||
|
private final double screenImageYRatio;
|
||||||
|
|
||||||
|
PICTMetadata(final int version, final double screenImageXRatio, final double screenImageYRatio) {
|
||||||
|
this.version = version;
|
||||||
|
this.screenImageXRatio = screenImageXRatio;
|
||||||
|
this.screenImageYRatio = screenImageYRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IIOMetadataNode getStandardChromaNode() {
|
||||||
|
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||||
|
|
||||||
|
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||||
|
chroma.appendChild(csType);
|
||||||
|
csType.setAttribute("name", "RGB");
|
||||||
|
|
||||||
|
// NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
|
||||||
|
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||||
|
chroma.appendChild(numChannels);
|
||||||
|
numChannels.setAttribute("value", "3");
|
||||||
|
|
||||||
|
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||||
|
chroma.appendChild(blackIsZero);
|
||||||
|
blackIsZero.setAttribute("value", "TRUE");
|
||||||
|
|
||||||
|
return chroma;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IIOMetadataNode getStandardDimensionNode() {
|
||||||
|
if (screenImageXRatio > 0.0d && screenImageYRatio > 0.0d) {
|
||||||
|
IIOMetadataNode node = new IIOMetadataNode("Dimension");
|
||||||
|
double ratio = screenImageXRatio / screenImageYRatio;
|
||||||
|
IIOMetadataNode subNode = new IIOMetadataNode("PixelAspectRatio");
|
||||||
|
subNode.setAttribute("value", "" + ratio);
|
||||||
|
node.appendChild(subNode);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IIOMetadataNode getStandardDataNode() {
|
||||||
|
IIOMetadataNode data = new IIOMetadataNode("Data");
|
||||||
|
|
||||||
|
// As this is a vector-ish format, with possibly multiple regions of pixel data, this makes no sense... :-P
|
||||||
|
// This is, however, consistent with the getRawImageTyp/getImageTypes
|
||||||
|
|
||||||
|
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||||
|
planarConfiguration.setAttribute("value", "PixelInterleaved");
|
||||||
|
data.appendChild(planarConfiguration);
|
||||||
|
|
||||||
|
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||||
|
sampleFormat.setAttribute("value", "UnsignedIntegral");
|
||||||
|
data.appendChild(sampleFormat);
|
||||||
|
|
||||||
|
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||||
|
bitsPerSample.setAttribute("value", "32");
|
||||||
|
data.appendChild(bitsPerSample);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IIOMetadataNode getStandardDocumentNode() {
|
||||||
|
IIOMetadataNode document = new IIOMetadataNode("Document");
|
||||||
|
|
||||||
|
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
|
||||||
|
document.appendChild(formatVersion);
|
||||||
|
formatVersion.setAttribute("value", Integer.toString(version));
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,7 @@ import java.awt.image.IndexColorModel;
|
|||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.charset.UnsupportedCharsetException;
|
import java.nio.charset.UnsupportedCharsetException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,7 +77,8 @@ final class PICTUtil {
|
|||||||
static String readIdString(final DataInput pStream) throws IOException {
|
static String readIdString(final DataInput pStream) throws IOException {
|
||||||
byte[] bytes = new byte[4];
|
byte[] bytes = new byte[4];
|
||||||
pStream.readFully(bytes);
|
pStream.readFully(bytes);
|
||||||
return new String(bytes, "ASCII");
|
|
||||||
|
return new String(bytes, StandardCharsets.US_ASCII);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,12 +30,16 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.pict;
|
package com.twelvemonkeys.imageio.plugins.pict;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.plugins.pict.QuickTime.ImageDesc;
|
||||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||||
import com.twelvemonkeys.io.LittleEndianDataOutputStream;
|
import com.twelvemonkeys.io.LittleEndianDataOutputStream;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.SequenceInputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QTBMPDecompressor
|
* QTBMPDecompressor
|
||||||
@ -45,28 +49,24 @@ import java.io.*;
|
|||||||
* @version $Id: QTBMPDecompressor.java,v 1.0 Feb 16, 2009 9:18:28 PM haraldk Exp$
|
* @version $Id: QTBMPDecompressor.java,v 1.0 Feb 16, 2009 9:18:28 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
final class QTBMPDecompressor extends QTDecompressor {
|
final class QTBMPDecompressor extends QTDecompressor {
|
||||||
public boolean canDecompress(final QuickTime.ImageDesc pDescription) {
|
public boolean canDecompress(final ImageDesc description) {
|
||||||
return QuickTime.VENDOR_APPLE.equals(pDescription.compressorVendor) && "WRLE".equals(pDescription.compressorIdentifer)
|
return QuickTime.VENDOR_APPLE.equals(description.compressorVendor)
|
||||||
&& "bmp ".equals(idString(pDescription.extraDesc, 4));
|
&& "WRLE".equals(description.compressorIdentifer)
|
||||||
|
&& "bmp ".equals(idString(description.extraDesc, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String idString(final byte[] pData, final int pOffset) {
|
private static String idString(final byte[] data, final int offset) {
|
||||||
try {
|
return new String(data, offset, 4, StandardCharsets.US_ASCII);
|
||||||
return new String(pData, pOffset, 4, "ASCII");
|
|
||||||
}
|
|
||||||
catch (UnsupportedEncodingException e) {
|
|
||||||
throw new Error("ASCII charset must always be supported", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferedImage decompress(final QuickTime.ImageDesc pDescription, final InputStream pStream) throws IOException {
|
public BufferedImage decompress(final ImageDesc description, final InputStream stream) throws IOException {
|
||||||
return ImageIO.read(new SequenceInputStream(fakeBMPHeader(pDescription), pStream));
|
return ImageIO.read(new SequenceInputStream(fakeBMPHeader(description), stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream fakeBMPHeader(final QuickTime.ImageDesc pDescription) throws IOException {
|
private InputStream fakeBMPHeader(final ImageDesc description) throws IOException {
|
||||||
int bmpHeaderSize = 14;
|
int bmpHeaderSize = 14;
|
||||||
int dibHeaderSize = 12; // 12: OS/2 V1
|
int dibHeaderSize = 12; // 12: OS/2 V1
|
||||||
ByteArrayOutputStream out = new FastByteArrayOutputStream(bmpHeaderSize + dibHeaderSize);
|
FastByteArrayOutputStream out = new FastByteArrayOutputStream(bmpHeaderSize + dibHeaderSize);
|
||||||
|
|
||||||
LittleEndianDataOutputStream stream = new LittleEndianDataOutputStream(out);
|
LittleEndianDataOutputStream stream = new LittleEndianDataOutputStream(out);
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ final class QTBMPDecompressor extends QTDecompressor {
|
|||||||
stream.writeByte('B');
|
stream.writeByte('B');
|
||||||
stream.writeByte('M');
|
stream.writeByte('M');
|
||||||
|
|
||||||
stream.writeInt(pDescription.dataSize + bmpHeaderSize + dibHeaderSize); // Data size + BMP header + DIB header
|
stream.writeInt(description.dataSize + bmpHeaderSize + dibHeaderSize); // Data size + BMP header + DIB header
|
||||||
|
|
||||||
stream.writeShort(0x0); // Reserved
|
stream.writeShort(0x0); // Reserved
|
||||||
stream.writeShort(0x0); // Reserved
|
stream.writeShort(0x0); // Reserved
|
||||||
@ -84,12 +84,12 @@ final class QTBMPDecompressor extends QTDecompressor {
|
|||||||
// DIB header
|
// DIB header
|
||||||
stream.writeInt(dibHeaderSize); // DIB header size
|
stream.writeInt(dibHeaderSize); // DIB header size
|
||||||
|
|
||||||
stream.writeShort(pDescription.width);
|
stream.writeShort(description.width);
|
||||||
stream.writeShort(pDescription.height);
|
stream.writeShort(description.height);
|
||||||
|
|
||||||
stream.writeShort(1); // Planes, only legal value: 1
|
stream.writeShort(1); // Planes, only legal value: 1
|
||||||
stream.writeShort(pDescription.depth); // Bit depth
|
stream.writeShort(description.depth); // Bit depth
|
||||||
|
|
||||||
return new ByteArrayInputStream(out.toByteArray());
|
return out.createInputStream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.pict;
|
package com.twelvemonkeys.imageio.plugins.pict;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.plugins.pict.QuickTime.ImageDesc;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -46,20 +48,20 @@ abstract class QTDecompressor {
|
|||||||
* Returns whether this decompressor is capable of decompressing the image
|
* Returns whether this decompressor is capable of decompressing the image
|
||||||
* data described by the given image description.
|
* data described by the given image description.
|
||||||
*
|
*
|
||||||
* @param pDescription the image description ({@code 'idsc'} Atom).
|
* @param description the image description ({@code 'idsc'} Atom).
|
||||||
* @return {@code true} if this decompressor is capable of decompressing
|
* @return {@code true} if this decompressor is capable of decompressing
|
||||||
* he data in the given image description, otherwise {@code false}.
|
* he data in the given image description, otherwise {@code false}.
|
||||||
*/
|
*/
|
||||||
public abstract boolean canDecompress(QuickTime.ImageDesc pDescription);
|
public abstract boolean canDecompress(ImageDesc description);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decompresses an image.
|
* Decompresses an image.
|
||||||
*
|
*
|
||||||
* @param pDescription the image description ({@code 'idsc'} Atom).
|
* @param description the image description ({@code 'idsc'} Atom).
|
||||||
* @param pStream the image data stream
|
* @param stream the image data stream
|
||||||
* @return the decompressed image
|
* @return the decompressed image
|
||||||
*
|
*
|
||||||
* @throws java.io.IOException if an I/O exception occurs during reading.
|
* @throws java.io.IOException if an I/O exception occurs during reading.
|
||||||
*/
|
*/
|
||||||
public abstract BufferedImage decompress(QuickTime.ImageDesc pDescription, InputStream pStream) throws IOException;
|
public abstract BufferedImage decompress(ImageDesc description, InputStream stream) throws IOException;
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,14 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.pict;
|
package com.twelvemonkeys.imageio.plugins.pict;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.plugins.pict.QuickTime.ImageDesc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QTGenericDecompressor
|
* QTGenericDecompressor
|
||||||
@ -43,11 +48,36 @@ import java.io.InputStream;
|
|||||||
* @version $Id: QTGenericDecompressor.java,v 1.0 Feb 16, 2009 9:26:13 PM haraldk Exp$
|
* @version $Id: QTGenericDecompressor.java,v 1.0 Feb 16, 2009 9:26:13 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
final class QTGenericDecompressor extends QTDecompressor {
|
final class QTGenericDecompressor extends QTDecompressor {
|
||||||
public boolean canDecompress(final QuickTime.ImageDesc pDescription) {
|
public boolean canDecompress(final ImageDesc description) {
|
||||||
|
// Instead of testing, we just allow everything, and might eventually fail on decompress later...
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferedImage decompress(final QuickTime.ImageDesc pDescription, final InputStream pStream) throws IOException {
|
public BufferedImage decompress(final ImageDesc description, final InputStream stream) throws IOException {
|
||||||
return ImageIO.read(pStream);
|
BufferedImage image = ImageIO.read(stream);
|
||||||
|
|
||||||
|
if (image == null) {
|
||||||
|
return readUsingFormatName(description.compressorIdentifer.trim(), stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BufferedImage readUsingFormatName(final String formatName, final InputStream stream) throws IOException {
|
||||||
|
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(formatName);
|
||||||
|
|
||||||
|
if (readers.hasNext()) {
|
||||||
|
ImageReader reader = readers.next();
|
||||||
|
|
||||||
|
try (ImageInputStream input = ImageIO.createImageInputStream(stream)) {
|
||||||
|
reader.setInput(input);
|
||||||
|
return reader.read(0);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
reader.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,9 @@ import java.io.DataInputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.plugins.pict.QuickTime.ImageDesc;
|
||||||
|
import static com.twelvemonkeys.imageio.plugins.pict.QuickTime.VENDOR_APPLE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QTRAWDecompressor
|
* QTRAWDecompressor
|
||||||
*
|
*
|
||||||
@ -51,21 +54,17 @@ final class QTRAWDecompressor extends QTDecompressor {
|
|||||||
// - Have a look at com.sun.media.imageio.stream.RawImageInputStream...
|
// - Have a look at com.sun.media.imageio.stream.RawImageInputStream...
|
||||||
// TODO: Support different bit depths
|
// TODO: Support different bit depths
|
||||||
|
|
||||||
public boolean canDecompress(final QuickTime.ImageDesc pDescription) {
|
public boolean canDecompress(final ImageDesc description) {
|
||||||
return QuickTime.VENDOR_APPLE.equals(pDescription.compressorVendor)
|
return VENDOR_APPLE.equals(description.compressorVendor)
|
||||||
&& "raw ".equals(pDescription.compressorIdentifer)
|
&& "raw ".equals(description.compressorIdentifer)
|
||||||
&& (pDescription.depth == 24 || pDescription.depth == 32);
|
&& (description.depth == 24 || description.depth == 32 || description.depth == 40);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferedImage decompress(final QuickTime.ImageDesc pDescription, final InputStream pStream) throws IOException {
|
public BufferedImage decompress(final ImageDesc description, final InputStream stream) throws IOException {
|
||||||
byte[] data = new byte[pDescription.dataSize];
|
byte[] data = new byte[description.dataSize];
|
||||||
|
|
||||||
DataInputStream stream = new DataInputStream(pStream);
|
try (DataInputStream dataStream = new DataInputStream(stream)) {
|
||||||
try {
|
dataStream.readFully(data, 0, description.dataSize);
|
||||||
stream.readFully(data, 0, pDescription.dataSize);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
stream.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DataBuffer buffer = new DataBufferByte(data, data.length);
|
DataBuffer buffer = new DataBufferByte(data, data.length);
|
||||||
@ -73,12 +72,12 @@ final class QTRAWDecompressor extends QTDecompressor {
|
|||||||
WritableRaster raster;
|
WritableRaster raster;
|
||||||
|
|
||||||
// TODO: Depth parameter can be 1-32 (color) or 33-40 (gray scale)
|
// TODO: Depth parameter can be 1-32 (color) or 33-40 (gray scale)
|
||||||
switch (pDescription.depth) {
|
switch (description.depth) {
|
||||||
case 40: // 8 bit gray (untested)
|
case 40: // 8 bit gray (untested)
|
||||||
raster = Raster.createInterleavedRaster(
|
raster = Raster.createInterleavedRaster(
|
||||||
buffer,
|
buffer,
|
||||||
pDescription.width, pDescription.height,
|
description.width, description.height,
|
||||||
pDescription.width, 1,
|
description.width, 1,
|
||||||
new int[] {0},
|
new int[] {0},
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
@ -86,8 +85,8 @@ final class QTRAWDecompressor extends QTDecompressor {
|
|||||||
case 24: // 24 bit RGB
|
case 24: // 24 bit RGB
|
||||||
raster = Raster.createInterleavedRaster(
|
raster = Raster.createInterleavedRaster(
|
||||||
buffer,
|
buffer,
|
||||||
pDescription.width, pDescription.height,
|
description.width, description.height,
|
||||||
pDescription.width * 3, 3,
|
description.width * 3, 3,
|
||||||
new int[] {0, 1, 2},
|
new int[] {0, 1, 2},
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
@ -96,9 +95,9 @@ final class QTRAWDecompressor extends QTDecompressor {
|
|||||||
// WORKAROUND: There is a bug in the way Java 2D interprets the band offsets in
|
// WORKAROUND: There is a bug in the way Java 2D interprets the band offsets in
|
||||||
// Raster.createInterleavedRaster (see below) before Java 6. So, instead of
|
// Raster.createInterleavedRaster (see below) before Java 6. So, instead of
|
||||||
// passing a correct offset array below, we swap channel 1 & 3 to make it ABGR...
|
// passing a correct offset array below, we swap channel 1 & 3 to make it ABGR...
|
||||||
for (int y = 0; y < pDescription.height; y++) {
|
for (int y = 0; y < description.height; y++) {
|
||||||
for (int x = 0; x < pDescription.width; x++) {
|
for (int x = 0; x < description.width; x++) {
|
||||||
int offset = 4 * y * pDescription.width + x * 4;
|
int offset = 4 * y * description.width + x * 4;
|
||||||
byte temp = data[offset + 1];
|
byte temp = data[offset + 1];
|
||||||
data[offset + 1] = data[offset + 3];
|
data[offset + 1] = data[offset + 3];
|
||||||
data[offset + 3] = temp;
|
data[offset + 3] = temp;
|
||||||
@ -107,21 +106,21 @@ final class QTRAWDecompressor extends QTDecompressor {
|
|||||||
|
|
||||||
raster = Raster.createInterleavedRaster(
|
raster = Raster.createInterleavedRaster(
|
||||||
buffer,
|
buffer,
|
||||||
pDescription.width, pDescription.height,
|
description.width, description.height,
|
||||||
pDescription.width * 4, 4,
|
description.width * 4, 4,
|
||||||
new int[] {3, 2, 1, 0}, // B & R mixed up. {1, 2, 3, 0} is correct
|
new int[] {3, 2, 1, 0}, // B & R mixed up. {1, 2, 3, 0} is correct
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IIOException("Unsupported RAW depth: " + pDescription.depth);
|
throw new IIOException("Unsupported QuickTime RAW depth: " + description.depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorModel cm = new ComponentColorModel(
|
ColorModel cm = new ComponentColorModel(
|
||||||
pDescription.depth <= 32 ? ColorSpace.getInstance(ColorSpace.CS_sRGB) : ColorSpace.getInstance(ColorSpace.CS_GRAY),
|
description.depth <= 32 ? ColorSpace.getInstance(ColorSpace.CS_sRGB) : ColorSpace.getInstance(ColorSpace.CS_GRAY),
|
||||||
pDescription.depth == 32,
|
description.depth == 32,
|
||||||
false,
|
false,
|
||||||
pDescription.depth == 32 ? Transparency.TRANSLUCENT : Transparency.OPAQUE,
|
description.depth == 32 ? Transparency.TRANSLUCENT : Transparency.OPAQUE,
|
||||||
DataBuffer.TYPE_BYTE
|
DataBuffer.TYPE_BYTE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ final class QuickTime {
|
|||||||
private static final List<QTDecompressor> sDecompressors = Arrays.asList(
|
private static final List<QTDecompressor> sDecompressors = Arrays.asList(
|
||||||
new QTBMPDecompressor(),
|
new QTBMPDecompressor(),
|
||||||
new QTRAWDecompressor(),
|
new QTRAWDecompressor(),
|
||||||
// The GenericDecompressor must be the last in the list
|
// The GenericDecompressor MUST be the last in the list, as it claims to read everything...
|
||||||
new QTGenericDecompressor()
|
new QTGenericDecompressor()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ final class QuickTime {
|
|||||||
kH263CodecType ='h263'
|
kH263CodecType ='h263'
|
||||||
kIndeo4CodecType ='IV41'
|
kIndeo4CodecType ='IV41'
|
||||||
kJPEGCodecType ='jpeg' -> JPEG, SUPPORTED
|
kJPEGCodecType ='jpeg' -> JPEG, SUPPORTED
|
||||||
kMacPaintCodecType ='PNTG' -> Isn't this the PICT format itself? Does that make sense?! ;-)
|
kMacPaintCodecType ='PNTG' -> PNTG, should work, but lacks test data
|
||||||
kMicrosoftVideo1CodecType ='msvc'
|
kMicrosoftVideo1CodecType ='msvc'
|
||||||
kMotionJPEGACodecType ='mjpa'
|
kMotionJPEGACodecType ='mjpa'
|
||||||
kMotionJPEGBCodecType ='mjpb'
|
kMotionJPEGBCodecType ='mjpb'
|
||||||
@ -99,12 +99,12 @@ final class QuickTime {
|
|||||||
kQuickDrawCodecType ='qdrw' -> QD?
|
kQuickDrawCodecType ='qdrw' -> QD?
|
||||||
kQuickDrawGXCodecType ='qdgx' -> QD?
|
kQuickDrawGXCodecType ='qdgx' -> QD?
|
||||||
kRawCodecType ='raw ' -> Raw (A)RGB pixel data
|
kRawCodecType ='raw ' -> Raw (A)RGB pixel data
|
||||||
kSGICodecType ='.SGI'
|
kSGICodecType ='.SGI' -> SGI, should work, but lacks test data
|
||||||
k16GrayCodecType ='b16g' -> Raw 16 bit gray data?
|
k16GrayCodecType ='b16g' -> Raw 16 bit gray data?
|
||||||
k64ARGBCodecType ='b64a' -> Raw 64 bit (16 bpp) color data?
|
k64ARGBCodecType ='b64a' -> Raw 64 bit (16 bpp) color data?
|
||||||
kSorensonCodecType ='SVQ1'
|
kSorensonCodecType ='SVQ1'
|
||||||
kSorensonYUV9CodecType ='syv9'
|
kSorensonYUV9CodecType ='syv9'
|
||||||
kTargaCodecType ='tga ' -> TGA, maybe create a plugin for that
|
kTargaCodecType ='tga ' -> TGA, should work, but lacks test data
|
||||||
k32AlphaGrayCodecType ='b32a' -> 16 bit gray + 16 bit alpha raw data?
|
k32AlphaGrayCodecType ='b32a' -> 16 bit gray + 16 bit alpha raw data?
|
||||||
kTIFFCodecType ='tiff' -> TIFF, SUPPORTED
|
kTIFFCodecType ='tiff' -> TIFF, SUPPORTED
|
||||||
kVectorCodecType ='path'
|
kVectorCodecType ='path'
|
||||||
@ -117,13 +117,13 @@ final class QuickTime {
|
|||||||
/**
|
/**
|
||||||
* Gets a decompressor that can decompress the described data.
|
* Gets a decompressor that can decompress the described data.
|
||||||
*
|
*
|
||||||
* @param pDescription the image description ({@code 'idsc'} Atom).
|
* @param description the image description ({@code 'idsc'} Atom).
|
||||||
* @return a decompressor that can decompress data decribed by the given {@link ImageDesc description},
|
* @return a decompressor that can decompress data decribed by the given {@link ImageDesc description},
|
||||||
* or {@code null} if no decompressor is found
|
* or {@code null} if no decompressor is found
|
||||||
*/
|
*/
|
||||||
private static QTDecompressor getDecompressor(final ImageDesc pDescription) {
|
private static QTDecompressor getDecompressor(final ImageDesc description) {
|
||||||
for (QTDecompressor decompressor : sDecompressors) {
|
for (QTDecompressor decompressor : sDecompressors) {
|
||||||
if (decompressor.canDecompress(pDescription)) {
|
if (decompressor.canDecompress(description)) {
|
||||||
return decompressor;
|
return decompressor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,13 +134,13 @@ final class QuickTime {
|
|||||||
/**
|
/**
|
||||||
* Decompresses the QuickTime image data from the given stream.
|
* Decompresses the QuickTime image data from the given stream.
|
||||||
*
|
*
|
||||||
* @param pStream the image input stream
|
* @param stream the image input stream
|
||||||
* @return a {@link BufferedImage} containing the image data, or {@code null} if no decompressor is capable of
|
* @return a {@link BufferedImage} containing the image data, or {@code null} if no decompressor is capable of
|
||||||
* decompressing the image.
|
* decompressing the image.
|
||||||
* @throws IOException if an I/O exception occurs during read
|
* @throws IOException if an I/O exception occurs during read
|
||||||
*/
|
*/
|
||||||
public static BufferedImage decompress(final ImageInputStream pStream) throws IOException {
|
public static BufferedImage decompress(final ImageInputStream stream) throws IOException {
|
||||||
ImageDesc description = ImageDesc.read(pStream);
|
ImageDesc description = ImageDesc.read(stream);
|
||||||
|
|
||||||
if (PICTImageReader.DEBUG) {
|
if (PICTImageReader.DEBUG) {
|
||||||
System.out.println(description);
|
System.out.println(description);
|
||||||
@ -152,13 +152,9 @@ final class QuickTime {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputStream streamAdapter = IIOUtil.createStreamAdapter(pStream, description.dataSize);
|
try (InputStream streamAdapter = IIOUtil.createStreamAdapter(stream, description.dataSize)) {
|
||||||
try {
|
|
||||||
return decompressor.decompress(description, streamAdapter);
|
return decompressor.decompress(description, streamAdapter);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
streamAdapter.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,7 +191,7 @@ final class QuickTime {
|
|||||||
|
|
||||||
byte[] extraDesc;
|
byte[] extraDesc;
|
||||||
|
|
||||||
private ImageDesc() {}
|
ImageDesc() {}
|
||||||
|
|
||||||
public static ImageDesc read(final DataInput pStream) throws IOException {
|
public static ImageDesc read(final DataInput pStream) throws IOException {
|
||||||
// The following looks like the 'idsc' Atom (as described in the QuickTime File Format)
|
// The following looks like the 'idsc' Atom (as described in the QuickTime File Format)
|
||||||
|
@ -0,0 +1,146 @@
|
|||||||
|
package com.twelvemonkeys.imageio.plugins.pntg;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||||
|
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||||
|
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||||
|
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
||||||
|
|
||||||
|
import javax.imageio.IIOException;
|
||||||
|
import javax.imageio.ImageReadParam;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.*;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.plugins.pntg.PNTGImageReaderSpi.isMacBinaryPNTG;
|
||||||
|
import static com.twelvemonkeys.imageio.util.IIOUtil.subsampleRow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PNTGImageReader.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: PNTGImageReader.java,v 1.0 23/03/2021 haraldk Exp$
|
||||||
|
*/
|
||||||
|
public final class PNTGImageReader extends ImageReaderBase {
|
||||||
|
|
||||||
|
private static final Set<ImageTypeSpecifier> IMAGE_TYPES =
|
||||||
|
Collections.singleton(ImageTypeSpecifiers.createIndexed(new int[] {-1, 0}, false, -1, 1, DataBuffer.TYPE_BYTE));
|
||||||
|
|
||||||
|
protected PNTGImageReader(final ImageReaderSpi provider) {
|
||||||
|
super(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void resetMembers() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWidth(final int imageIndex) throws IOException {
|
||||||
|
checkBounds(imageIndex);
|
||||||
|
|
||||||
|
return 576;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeight(final int imageIndex) throws IOException {
|
||||||
|
checkBounds(imageIndex);
|
||||||
|
|
||||||
|
return 720;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<ImageTypeSpecifier> getImageTypes(final int imageIndex) throws IOException {
|
||||||
|
checkBounds(imageIndex);
|
||||||
|
|
||||||
|
return IMAGE_TYPES.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BufferedImage read(final int imageIndex, final ImageReadParam param) throws IOException {
|
||||||
|
checkBounds(imageIndex);
|
||||||
|
readHeader();
|
||||||
|
|
||||||
|
int width = getWidth(imageIndex);
|
||||||
|
int height = getHeight(imageIndex);
|
||||||
|
|
||||||
|
BufferedImage destination = getDestination(param, getImageTypes(imageIndex), width, height);
|
||||||
|
int[] destBands = param != null ? param.getDestinationBands() : null;
|
||||||
|
|
||||||
|
Rectangle srcRegion = new Rectangle();
|
||||||
|
Rectangle destRegion = new Rectangle();
|
||||||
|
computeRegions(param, width, height, destination, srcRegion, destRegion);
|
||||||
|
|
||||||
|
int xSub = param != null ? param.getSourceXSubsampling() : 1;
|
||||||
|
int ySub = param != null ? param.getSourceYSubsampling() : 1;
|
||||||
|
|
||||||
|
WritableRaster destRaster = destination.getRaster()
|
||||||
|
.createWritableChild(destRegion.x, destRegion.y, destRegion.width, destRegion.height, 0, 0, destBands);
|
||||||
|
|
||||||
|
Raster rowRaster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, width, 1, 1, 1, null)
|
||||||
|
.createChild(srcRegion.x, 0, destRegion.width, 1, 0, 0, destBands);
|
||||||
|
|
||||||
|
processImageStarted(imageIndex);
|
||||||
|
|
||||||
|
readData(srcRegion, destRegion, xSub, ySub, destRaster, rowRaster);
|
||||||
|
|
||||||
|
processImageComplete();
|
||||||
|
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readData(Rectangle srcRegion, Rectangle destRegion, int xSub, int ySub, WritableRaster destRaster, Raster rowRaster) throws IOException {
|
||||||
|
byte[] rowData = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||||
|
|
||||||
|
try (DataInputStream decoderStream = new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(imageInput), new PackBitsDecoder()))) {
|
||||||
|
int srcMaxY = srcRegion.y + srcRegion.height;
|
||||||
|
for (int y = 0; y < srcMaxY; y++) {
|
||||||
|
decoderStream.readFully(rowData);
|
||||||
|
|
||||||
|
if (y >= srcRegion.y && y % ySub == 0) {
|
||||||
|
subsampleRow(rowData, srcRegion.x, srcRegion.width, rowData, destRegion.x, 1, 1, xSub);
|
||||||
|
|
||||||
|
int destY = (y - srcRegion.y) / ySub;
|
||||||
|
destRaster.setDataElements(0, destY, rowRaster);
|
||||||
|
|
||||||
|
processImageProgress(y / (float) srcMaxY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abortRequested()) {
|
||||||
|
processReadAborted();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||||
|
checkBounds(imageIndex);
|
||||||
|
|
||||||
|
return new PNTGMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readHeader() throws IOException {
|
||||||
|
if (isMacBinaryPNTG(imageInput)) {
|
||||||
|
// Seek to end of MacBinary header
|
||||||
|
// TODO: Could actually get the file name, creation date etc metadata from this data
|
||||||
|
imageInput.seek(128);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
imageInput.seek(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip pattern data section (usually all 0s)
|
||||||
|
if (imageInput.skipBytes(512) != 512) {
|
||||||
|
throw new IIOException("Could not skip pattern data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package com.twelvemonkeys.imageio.plugins.pntg;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||||
|
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PNTGImageReaderSpi.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: PNTGImageReaderSpi.java,v 1.0 23/03/2021 haraldk Exp$
|
||||||
|
*/
|
||||||
|
public final class PNTGImageReaderSpi extends ImageReaderSpiBase {
|
||||||
|
public PNTGImageReaderSpi() {
|
||||||
|
super(new PNTGProviderInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canDecodeInput(final Object source) throws IOException {
|
||||||
|
if (!(source instanceof ImageInputStream)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageInputStream stream = (ImageInputStream) source;
|
||||||
|
stream.mark();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: Figure out how to read the files without the MacBinary header...
|
||||||
|
// Probably not possible, as it's just 512 bytes of nulls OR pattern information
|
||||||
|
return isMacBinaryPNTG(stream);
|
||||||
|
}
|
||||||
|
catch (EOFException ignore) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
stream.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isMacBinaryPNTG(final ImageInputStream stream) throws IOException {
|
||||||
|
stream.seek(0);
|
||||||
|
|
||||||
|
if (stream.readByte() != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte nameLen = stream.readByte();
|
||||||
|
if (nameLen < 0 || nameLen > 63) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skipBytes(63);
|
||||||
|
|
||||||
|
// Validate that type is PNTG and that next 4 bytes are all within the ASCII range, typically 'MPNT'
|
||||||
|
return stream.readInt() == ('P' << 24 | 'N' << 16 | 'T' << 8 | 'G') && (stream.readInt() & 0x80808080) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PNTGImageReader createReaderInstance(final Object extension) {
|
||||||
|
return new PNTGImageReader(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription(final Locale locale) {
|
||||||
|
return "Apple MacPaint Painting (PNTG) image reader";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package com.twelvemonkeys.imageio.plugins.pntg;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||||
|
|
||||||
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PNTGMetadata.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: PNTGMetadata.java,v 1.0 23/03/2021 haraldk Exp$
|
||||||
|
*/
|
||||||
|
public class PNTGMetadata extends AbstractMetadata {
|
||||||
|
@Override
|
||||||
|
protected IIOMetadataNode getStandardChromaNode() {
|
||||||
|
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||||
|
|
||||||
|
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||||
|
chroma.appendChild(csType);
|
||||||
|
csType.setAttribute("name", "GRAY");
|
||||||
|
|
||||||
|
// NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
|
||||||
|
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||||
|
chroma.appendChild(numChannels);
|
||||||
|
numChannels.setAttribute("value", "1");
|
||||||
|
|
||||||
|
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||||
|
chroma.appendChild(blackIsZero);
|
||||||
|
blackIsZero.setAttribute("value", "FALSE");
|
||||||
|
|
||||||
|
return chroma;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IIOMetadataNode getStandardCompressionNode() {
|
||||||
|
IIOMetadataNode compressionNode = new IIOMetadataNode("Compression");
|
||||||
|
|
||||||
|
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||||
|
compressionTypeName.setAttribute("value", "PackBits"); // RLE?
|
||||||
|
compressionNode.appendChild(compressionTypeName);
|
||||||
|
compressionNode.appendChild(new IIOMetadataNode("Lossless"));
|
||||||
|
// "value" defaults to TRUE
|
||||||
|
|
||||||
|
return compressionNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IIOMetadataNode getStandardDataNode() {
|
||||||
|
IIOMetadataNode data = new IIOMetadataNode("Data");
|
||||||
|
|
||||||
|
// PlanarConfiguration
|
||||||
|
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||||
|
planarConfiguration.setAttribute("value", "PixelInterleaved");
|
||||||
|
data.appendChild(planarConfiguration);
|
||||||
|
|
||||||
|
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||||
|
sampleFormat.setAttribute("value", "UnsignedIntegral");
|
||||||
|
data.appendChild(sampleFormat);
|
||||||
|
|
||||||
|
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||||
|
bitsPerSample.setAttribute("value", "1");
|
||||||
|
data.appendChild(bitsPerSample);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IIOMetadataNode getStandardDocumentNode() {
|
||||||
|
IIOMetadataNode document = new IIOMetadataNode("Document");
|
||||||
|
|
||||||
|
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
|
||||||
|
document.appendChild(formatVersion);
|
||||||
|
formatVersion.setAttribute("value", "1.0");
|
||||||
|
|
||||||
|
// TODO: We could get the file creation time from MacBinary header here...
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IIOMetadataNode getStandardTextNode() {
|
||||||
|
// TODO: We could get the file name from MacBinary header here...
|
||||||
|
return super.getStandardTextNode();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015, 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 of the copyright holder 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 HOLDER 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.pntg;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PNTGProviderInfo.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: harald.kuhr$
|
||||||
|
* @version $Id: PNTGProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
|
||||||
|
*/
|
||||||
|
final class PNTGProviderInfo extends ReaderWriterProviderInfo {
|
||||||
|
protected PNTGProviderInfo() {
|
||||||
|
super(
|
||||||
|
PNTGProviderInfo.class,
|
||||||
|
new String[] {"pntg", "PNTG"},
|
||||||
|
new String[] {"mac", "pic", "pntg"},
|
||||||
|
new String[] {"image/x-pntg"},
|
||||||
|
"com.twelvemonkeys.imageio.plugins.mac.MACImageReader",
|
||||||
|
new String[] {"com.twelvemonkeys.imageio.plugins.mac.MACImageReaderSpi"},
|
||||||
|
null, null,
|
||||||
|
false, null, null, null, null,
|
||||||
|
true, null, null, null, null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,2 @@
|
|||||||
|
com.twelvemonkeys.imageio.plugins.pntg.PNTGImageReaderSpi
|
||||||
com.twelvemonkeys.imageio.plugins.pict.PICTImageReaderSpi
|
com.twelvemonkeys.imageio.plugins.pict.PICTImageReaderSpi
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.twelvemonkeys.imageio.plugins.pict;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.plugins.pict.QuickTime.ImageDesc;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QTBMPDecompressorTest.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: QTBMPDecompressorTest.java,v 1.0 24/03/2021 haraldk Exp$
|
||||||
|
*/
|
||||||
|
public class QTBMPDecompressorTest {
|
||||||
|
@Test
|
||||||
|
public void canDecompress() {
|
||||||
|
QTDecompressor decompressor = new QTBMPDecompressor();
|
||||||
|
|
||||||
|
ImageDesc description = new ImageDesc();
|
||||||
|
description.compressorVendor = QuickTime.VENDOR_APPLE;
|
||||||
|
description.compressorIdentifer = "WRLE";
|
||||||
|
description.extraDesc = "....bmp ...something...".getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
assertTrue(decompressor.canDecompress(description));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.twelvemonkeys.imageio.plugins.pict;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.plugins.pict.QuickTime.ImageDesc;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QTBMPDecompressorTest.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: QTBMPDecompressorTest.java,v 1.0 24/03/2021 haraldk Exp$
|
||||||
|
*/
|
||||||
|
public class QTGenericDecompressorTest {
|
||||||
|
private ImageDesc createDescription(final String identifer, final String name, final int depth) {
|
||||||
|
ImageDesc description = new ImageDesc();
|
||||||
|
description.compressorVendor = QuickTime.VENDOR_APPLE;
|
||||||
|
description.compressorIdentifer = identifer;
|
||||||
|
description.compressorName = name;
|
||||||
|
description.depth = (short) depth;
|
||||||
|
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canDecompressJPEG() {
|
||||||
|
QTDecompressor decompressor = new QTGenericDecompressor();
|
||||||
|
|
||||||
|
assertTrue(decompressor.canDecompress(createDescription("jpeg", "Photo - JPEG", 8)));
|
||||||
|
assertTrue(decompressor.canDecompress(createDescription("jpeg", "Photo - JPEG", 24)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canDecompressPNG() {
|
||||||
|
QTDecompressor decompressor = new QTGenericDecompressor();
|
||||||
|
|
||||||
|
assertTrue(decompressor.canDecompress(createDescription("png ", "PNG", 8)));
|
||||||
|
assertTrue(decompressor.canDecompress(createDescription("png ", "PNG", 24)));
|
||||||
|
assertTrue(decompressor.canDecompress(createDescription("png ", "PNG", 32)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canDecompressTIFF() {
|
||||||
|
QTDecompressor decompressor = new QTGenericDecompressor();
|
||||||
|
|
||||||
|
assertTrue(decompressor.canDecompress(createDescription("tiff", "TIFF", 8)));
|
||||||
|
assertTrue(decompressor.canDecompress(createDescription("tiff", "TIFF", 24)));
|
||||||
|
assertTrue(decompressor.canDecompress(createDescription("tiff", "TIFF", 32)));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package com.twelvemonkeys.imageio.plugins.pict;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.plugins.pict.QuickTime.ImageDesc;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QTBMPDecompressorTest.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: QTBMPDecompressorTest.java,v 1.0 24/03/2021 haraldk Exp$
|
||||||
|
*/
|
||||||
|
public class QTRAWDecompressorTest {
|
||||||
|
private ImageDesc createDescription(int bitDepth) {
|
||||||
|
ImageDesc description = new ImageDesc();
|
||||||
|
description.compressorVendor = QuickTime.VENDOR_APPLE;
|
||||||
|
description.compressorIdentifer = "raw ";
|
||||||
|
description.depth = (short) bitDepth;
|
||||||
|
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canDecompressRGB() {
|
||||||
|
QTDecompressor decompressor = new QTRAWDecompressor();
|
||||||
|
|
||||||
|
assertTrue(decompressor.canDecompress(createDescription(24)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canDecompressRGBA() {
|
||||||
|
QTDecompressor decompressor = new QTRAWDecompressor();
|
||||||
|
|
||||||
|
assertTrue(decompressor.canDecompress(createDescription(32)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canDecompressGray() {
|
||||||
|
QTDecompressor decompressor = new QTRAWDecompressor();
|
||||||
|
|
||||||
|
assertTrue(decompressor.canDecompress(createDescription(40)));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.twelvemonkeys.imageio.plugins.pntg;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||||
|
|
||||||
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PNTGImageReaderTest.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: PNTGImageReaderTest.java,v 1.0 23/03/2021 haraldk Exp$
|
||||||
|
*/
|
||||||
|
public class PNTGImageReaderTest extends ImageReaderAbstractTest<PNTGImageReader> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ImageReaderSpi createProvider() {
|
||||||
|
return new PNTGImageReaderSpi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<TestData> getTestData() {
|
||||||
|
return Arrays.asList(new TestData(getClassLoaderResource("/mac/porsches.mac"), new Dimension(576, 720)),
|
||||||
|
new TestData(getClassLoaderResource("/mac/MARBLES.MAC"), new Dimension(576, 720)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> getFormatNames() {
|
||||||
|
return Arrays.asList("PNTG", "pntg");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> getSuffixes() {
|
||||||
|
return Arrays.asList("mac", "pntg");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> getMIMETypes() {
|
||||||
|
return Collections.singletonList("image/x-pntg");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testProviderCanRead() throws IOException {
|
||||||
|
// TODO: This a kind of hack...
|
||||||
|
// Currently, the provider don't claim to read the MARBLES.MAC image,
|
||||||
|
// as it lacks the MacBinary header and thus no way to identify format.
|
||||||
|
// We can still read it, so we'll include it in the other tests.
|
||||||
|
List<TestData> testData = getTestData().subList(0, 1);
|
||||||
|
|
||||||
|
for (TestData data : testData) {
|
||||||
|
ImageInputStream stream = data.getInputStream();
|
||||||
|
assertNotNull(stream);
|
||||||
|
assertTrue("Provider is expected to be able to decode data: " + data, provider.canDecodeInput(stream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.twelvemonkeys.imageio.plugins.pntg;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PNTGMetadataTest.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: PNTGMetadataTest.java,v 1.0 23/03/2021 haraldk Exp$
|
||||||
|
*/
|
||||||
|
public class PNTGMetadataTest {
|
||||||
|
@Test
|
||||||
|
public void testCreate() {
|
||||||
|
new PNTGMetadata();
|
||||||
|
}
|
||||||
|
}
|
BIN
imageio/imageio-pict/src/test/resources/mac/MARBLES.MAC
Normal file
BIN
imageio/imageio-pict/src/test/resources/mac/MARBLES.MAC
Normal file
Binary file not shown.
BIN
imageio/imageio-pict/src/test/resources/mac/porsches.mac
Normal file
BIN
imageio/imageio-pict/src/test/resources/mac/porsches.mac
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user