jj2000.j2k.fileformat.reader.FileFormatReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jai-imageio-jpeg2000 Show documentation
Show all versions of jai-imageio-jpeg2000 Show documentation
JPEG2000 support for Java Advanced Imaging Image I/O Tools API core.
This module is licensed under the [JJ2000 license](LICENSE.txt) and
is therefore NOT compatible with the GPL 3 license. It should be
compatible with the LGPL 2.1 license.
/*
* $RCSfile: FileFormatReader.java,v $
* $Revision: 1.2 $
* $Date: 2005/04/28 01:25:38 $
* $State: Exp $
*
* Class: FileFormatReader
*
* Description: Read J2K file stream
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package jj2000.j2k.fileformat.reader;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.io.EOFException;
import java.io.IOException;
import java.util.Vector;
import jj2000.j2k.codestream.Markers;
import jj2000.j2k.fileformat.FileFormatBoxes;
import jj2000.j2k.io.RandomAccessIO;
import com.github.jaiimageio.jpeg2000.impl.BitsPerComponentBox;
import com.github.jaiimageio.jpeg2000.impl.Box;
import com.github.jaiimageio.jpeg2000.impl.ChannelDefinitionBox;
import com.github.jaiimageio.jpeg2000.impl.ColorSpecificationBox;
import com.github.jaiimageio.jpeg2000.impl.ComponentMappingBox;
import com.github.jaiimageio.jpeg2000.impl.DataEntryURLBox;
import com.github.jaiimageio.jpeg2000.impl.FileTypeBox;
import com.github.jaiimageio.jpeg2000.impl.HeaderBox;
import com.github.jaiimageio.jpeg2000.impl.J2KMetadata;
import com.github.jaiimageio.jpeg2000.impl.PaletteBox;
import com.github.jaiimageio.jpeg2000.impl.ResolutionBox;
import com.github.jaiimageio.jpeg2000.impl.SignatureBox;
import com.github.jaiimageio.jpeg2000.impl.UUIDBox;
import com.github.jaiimageio.jpeg2000.impl.UUIDListBox;
import com.github.jaiimageio.jpeg2000.impl.XMLBox;
/**
* This class reads the file format wrapper that may or may not exist around a
* valid JPEG 2000 codestream. Since no information from the file format is
* used in the actual decoding, this class simply goes through the file and
* finds the first valid codestream.
*
* @see jj2000.j2k.fileformat.writer.FileFormatWriter
* */
public class FileFormatReader implements FileFormatBoxes{
/** The random access from which the file format boxes are read */
private RandomAccessIO in;
/** The positions of the codestreams in the fileformat*/
private Vector codeStreamPos;
/** The lengths of the codestreams in the fileformat*/
private Vector codeStreamLength;
/** Create a IndexColorModel from the palette box if there is one */
private ColorModel colorModel = null;
/** The meta data */
private J2KMetadata metadata;
/** Parameters in header box */
private int width;
private int height;
private int numComp;
private int bitDepth;
private int compressionType;
private int unknownColor;
private int intelProp;
/** Various bit depth */
private byte[] bitDepths;
/** The lut in the palette box */
private byte[][] lut;
private byte[] compSize;
/** Component mapping */
private short[] comps;
private byte[] type;
private byte[] maps;
/** Channel definitions */
private short[] channels ;
private short[] cType;
private short[] associations;
/** Color specification */
private int colorSpaceType;
/** ICC profile */
private ICC_Profile profile;
/**
* The constructor of the FileFormatReader
*
* @param in The RandomAccessIO from which to read the file format
* */
public FileFormatReader(RandomAccessIO in, J2KMetadata metadata){
this.in = in;
this.metadata = metadata;
}
/**
* This method checks whether the given RandomAccessIO is a valid JP2 file
* and if so finds the first codestream in the file. Currently, the
* information in the codestream is not used
*
* @param in The RandomAccessIO from which to read the file format
*
* @exception java.io.IOException If an I/O error ocurred.
*
* @exception java.io.EOFException If end of file is reached
* */
public void readFileFormat() throws IOException, EOFException {
int foundCodeStreamBoxes=0;
int box;
int length;
long longLength=0;
int pos;
short marker;
boolean jp2HeaderBoxFound=false;
boolean lastBoxFound = false;
try{
// Go through the randomaccessio and find the first
// contiguous codestream box. Check also that the File Format is
// correct
pos = in.getPos();
// Make sure that the first 12 bytes is the JP2_SIGNATURE_BOX
// or if not that the first 2 bytes is the SOC marker
if(in.readInt() != 0x0000000c ||
in.readInt() != JP2_SIGNATURE_BOX ||
in.readInt() != 0x0d0a870a){ // Not a JP2 file
in.seek(pos);
marker = (short)in.readShort();
if(marker != Markers.SOC) //Standard syntax marker found
throw new Error("File is neither valid JP2 file nor "+
"valid JPEG 2000 codestream");
in.seek(pos);
if(codeStreamPos == null)
codeStreamPos = new Vector();
codeStreamPos.addElement(new Integer(pos));
return;
}
if (metadata != null)
metadata.addNode(new SignatureBox());
// Read all remaining boxes
while(!lastBoxFound){
pos = in.getPos();
length = in.readInt();
if((pos+length) == in.length())
lastBoxFound = true;
box = in.readInt();
if (length == 0) {
lastBoxFound = true;
length = in.length()-in.getPos();
} else if(length == 1) {
longLength = in.readLong();
throw new IOException("File too long.");
} else longLength = (long) 0;
pos = in.getPos();
length -= 8;
switch(box){
case FILE_TYPE_BOX:
readFileTypeBox(length + 8, longLength);
break;
case CONTIGUOUS_CODESTREAM_BOX:
if(!jp2HeaderBoxFound)
throw new Error("Invalid JP2 file: JP2Header box not "+
"found before Contiguous codestream "+
"box ");
readContiguousCodeStreamBox(length + 8, longLength);
break;
case JP2_HEADER_BOX:
if(jp2HeaderBoxFound)
throw new Error("Invalid JP2 file: Multiple "+
"JP2Header boxes found");
readJP2HeaderBox(length + 8);
jp2HeaderBoxFound = true;
length = 0;
break;
case IMAGE_HEADER_BOX:
readImageHeaderBox(length);
break;
case INTELLECTUAL_PROPERTY_BOX:
readIntPropertyBox(length);
break;
case XML_BOX:
readXMLBox(length);
break;
case UUID_INFO_BOX:
length = 0;
break;
case UUID_BOX:
readUUIDBox(length);
break;
case UUID_LIST_BOX:
readUUIDListBox(length);
break;
case URL_BOX:
readURLBox(length);
break;
case PALETTE_BOX:
readPaletteBox(length + 8);
break;
case BITS_PER_COMPONENT_BOX:
readBitsPerComponentBox(length);
break;
case COMPONENT_MAPPING_BOX:
readComponentMappingBox(length);
break;
case COLOUR_SPECIFICATION_BOX:
readColourSpecificationBox(length);
break;
case CHANNEL_DEFINITION_BOX:
readChannelDefinitionBox(length);
break;
case RESOLUTION_BOX:
length = 0;
break;
case CAPTURE_RESOLUTION_BOX:
case DEFAULT_DISPLAY_RESOLUTION_BOX:
readResolutionBox(box, length);
break;
default:
if (metadata != null) {
byte[] data = new byte[length];
in.readFully(data, 0, length);
metadata.addNode(new Box(length + 8,
box,
longLength,
data));
}
}
if(!lastBoxFound)
in.seek(pos+length);
}
}catch( EOFException e ){
throw new Error("EOF reached before finding Contiguous "+
"Codestream Box");
}
if(codeStreamPos.size() == 0){
// Not a valid JP2 file or codestream
throw new Error("Invalid JP2 file: Contiguous codestream box "+
"missing");
}
return;
}
/**
* This method reads the File Type box
*
* @return false if the File Type box was not found or invalid else true
*
* @exception java.io.IOException If an I/O error ocurred.
*
* @exception java.io.EOFException If the end of file was reached
* */
public boolean readFileTypeBox(int length, long longLength)
throws IOException, EOFException {
int nComp;
boolean foundComp=false;
// Check for XLBox
if(length == 1) { // Box has 8 byte length;
longLength = in.readLong();
throw new IOException("File too long.");
}
// Check that this is a correct DBox value
// Read Brand field
if(in.readInt() != FT_BR)
return false;
// Read MinV field
int minorVersion = in.readInt();
// Check that there is at least one FT_BR entry in in
// compatibility list
nComp = (length - 16)/4; // Number of compatibilities.
int[] comp = new int[nComp];
for(int i=0; i < nComp; i++){
if((comp[i] = in.readInt()) == FT_BR)
foundComp = true;
}
if(!foundComp)
return false;
if (metadata != null)
metadata.addNode(new FileTypeBox(FT_BR, minorVersion, comp));
return true;
}
/**
* This method reads the JP2Header box
*
* @param pos The position in the file
*
* @param length The length of the JP2Header box
*
* @param long length The length of the JP2Header box if greater than
* 1<<32
*
* @return false if the JP2Header box was not found or invalid else true
*
* @exception java.io.IOException If an I/O error ocurred.
*
* @exception java.io.EOFException If the end of file was reached
* */
public boolean readJP2HeaderBox(int length)
throws IOException, EOFException {
if(length == 0) // This can not be last box
throw new Error("Zero-length of JP2Header Box");
// Here the JP2Header data (DBox) would be read if we were to use it
return true;
}
/**
* This method reads the Image Header box
* @param length The length of the JP2Header box
*
* @return false if the JP2Header box was not found or invalid else true
*
* @exception java.io.IOException If an I/O error ocurred.
*
* @exception java.io.EOFException If the end of file was reached
* */
public boolean readImageHeaderBox(int length)
throws IOException, EOFException {
if(length == 0) // This can not be last box
throw new Error("Zero-length of JP2Header Box");
// Here the JP2Header data (DBox) would be read if we were to use it
height = in.readInt();
width = in.readInt();
numComp = in.readShort();
bitDepth = in.readByte();
compressionType = in.readByte();
unknownColor = in.readByte();
intelProp = in.readByte();
if (metadata != null) {
metadata.addNode(new HeaderBox(height, width, numComp, bitDepth,
compressionType, unknownColor,
intelProp));
}
return true;
}
/**
* This method skips the Contiguous codestream box and adds position
* of contiguous codestream to a vector
*
* @param pos The position in the file
*
* @param length The length of the JP2Header box
*
* @param long length The length of the JP2Header box if greater than 1<<32
*
* @return false if the Contiguous codestream box was not found or invalid
* else true
*
* @exception java.io.IOException If an I/O error ocurred.
*
* @exception java.io.EOFException If the end of file was reached
* */
public boolean readContiguousCodeStreamBox(int length,
long longLength)
throws IOException, EOFException {
// Add new codestream position to position vector
int ccpos = in.getPos();
if(codeStreamPos == null)
codeStreamPos = new Vector();
codeStreamPos.addElement(new Integer(ccpos));
// Add new codestream length to length vector
if(codeStreamLength == null)
codeStreamLength = new Vector();
codeStreamLength.addElement(new Integer(length));
return true;
}
/**
* This method reads the contents of the Intellectual property box
* */
public void readIntPropertyBox(int length) throws IOException {
if (metadata != null) {
byte[] data = new byte[length];
in.readFully(data, 0, length);
metadata.addNode(new Box(length + 8, 0x6A703269, data));
}
}
/**
* This method reads the contents of the XML box
*/
public void readXMLBox(int length) throws IOException {
if (metadata != null) {
byte[] data = new byte[length];
in.readFully(data, 0, length);
metadata.addNode(new XMLBox(data));
}
}
/**
* This method reads the contents of the XML box
*/
public void readURLBox(int length) throws IOException {
if (metadata != null) {
byte[] data = new byte[length];
in.readFully(data, 0, length);
metadata.addNode(new DataEntryURLBox(data));
}
}
/**
* This method reads the contents of the Intellectual property box
*/
public void readUUIDBox(int length) throws IOException {
if (metadata != null) {
byte[] data = new byte[length];
in.readFully(data, 0, length);
metadata.addNode(new UUIDBox(data));
}
}
/**
* This method reads the contents of the UUID List box
* */
public void readUUIDListBox(int length) throws IOException {
if (metadata != null) {
byte[] data = new byte[length];
in.readFully(data, 0, length);
metadata.addNode(new UUIDListBox(data));
}
}
/** This method reads the content of the palette box */
public void readPaletteBox(int length) throws IOException {
// Get current position in file
int pos = in.getPos();
int lutSize = in.readShort();
int numComp = in.readByte();
compSize = new byte[numComp];
for (int i = 0; i < numComp; i++) {
compSize[i] = (byte)in.readByte();
}
lut = new byte[numComp][lutSize];
for (int n=0; n < lutSize; n++) {
for (int c = 0; c < numComp; c++) {
int depth = 1 + (compSize[c] & 0x7F);
if (depth > 32)
depth = 32;
int numBytes = (depth + 7)>>3;
int mask = (1 << depth) - 1;
byte[] buf = new byte[numBytes];
in.readFully(buf, 0, numBytes);
int val = 0;
for (int k = 0; k < numBytes; k++) {
val = buf[k] + (val << 8);
}
lut[c][n] = (byte)val;
}
}
if (metadata != null) {
metadata.addNode(new PaletteBox(length, compSize, lut));
}
}
/** Read the component mapping channel.
*/
public void readComponentMappingBox(int length)throws IOException {
int num = length / 4;
comps = new short[num];
type = new byte[num];
maps = new byte[num];
for (int i = 0; i < num; i++) {
comps[i] = in.readShort();
type[i] = in.readByte();
maps[i] = in.readByte();
}
if (metadata != null) {
metadata.addNode(new ComponentMappingBox(comps, type, maps));
}
}
/**
* This method reads the Channel Definition box
*
* @exception java.io.IOException If an I/O error ocurred.
*
*/
public void readChannelDefinitionBox(int length)throws IOException {
int num = in.readShort();
channels = new short[num];
cType = new short[num];
associations = new short[num];
for (int i = 0; i < num; i++) {
channels[i] = in.readShort();
cType[i] = in.readShort();
associations[i] = in.readShort();
}
if (metadata != null) {
metadata.addNode(new ChannelDefinitionBox(channels, cType, associations));
}
}
/** Read the bits per component.
*/
public void readBitsPerComponentBox(int length)throws IOException {
bitDepths = new byte[length];
in.readFully(bitDepths, 0, length);
if (metadata != null) {
metadata.addNode(new BitsPerComponentBox(bitDepths));
}
}
/** Read the color specifications.
*/
public void readColourSpecificationBox(int length)throws IOException {
// read METHOD field
byte method = (byte)in.readByte();
// read PREC field
byte prec = (byte)in.readByte();
// read APPROX field
byte approx = (byte)in.readByte();
if (method == 2) {
byte[] data = new byte[length - 3];
in.readFully(data, 0, data.length);
profile = ICC_Profile.getInstance(data);
} else // read EnumCS field
colorSpaceType = in.readInt();
if (metadata != null) {
metadata.addNode(new ColorSpecificationBox(method, prec, approx,
colorSpaceType,
profile));
}
}
/** Read the resolution.
*/
public void readResolutionBox(int type, int length)throws IOException {
byte[] data = new byte[length];
in.readFully(data, 0, length);
if (metadata != null) {
metadata.addNode(new ResolutionBox(type, data));
}
}
/**
* This method creates and returns an array of positions to contiguous
* codestreams in the file
*
* @return The positions of the contiguous codestreams in the file
* */
public long[] getCodeStreamPos(){
int size = codeStreamPos.size();
long[] pos = new long[size];
for(int i=0 ; i maxBitDepth)
maxBitDepth = bits[i];
int type = -1;
if (maxBitDepth <= 8)
type = DataBuffer.TYPE_BYTE;
else if (maxBitDepth <= 16)
type = isSigned ? DataBuffer.TYPE_SHORT : DataBuffer.TYPE_USHORT;
else if (maxBitDepth <= 32)
type = DataBuffer.TYPE_INT;
if (type == -1)
return null;
if (cs != null) {
colorModel = new ComponentColorModel(cs,
bits,
hasAlpha,
isPremultiplied[0],
hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE ,
type);
}
}
return colorModel;
}
}