/* * #%L * Fork of JAI Image I/O Tools. * %% * Copyright (C) 2008 - 2014 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. 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. * * 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 HOLDERS 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. * * The views and conclusions contained in the software and documentation are * those of the authors and should not be interpreted as representing official * policies, either expressed or implied, of any organization. * #L% */ /* * $RCSfile: CodestreamManipulator.java,v $ * $Revision: 1.1 $ * $Date: 2005/02/11 05:02:24 $ * $State: Exp $ * * Class: CodestreamManipulator * * Description: Manipulates codestream to create tile-parts etc * * * * 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.util; import jj2000.j2k.codestream.*; import jj2000.j2k.io.*; import java.util.*; import java.io.*; /** * This class takes a legal JPEG 2000 codestream and performs some * manipulation on it. Currently the manipulations supported are: Tile-parts * */ public class CodestreamManipulator{ /** Flag indicating whether packed packet headers in main header is used * */ private boolean ppmUsed; /** Flag indicating whether packed packet headers in tile headers is used * */ private boolean pptUsed; /** Flag indicating whether SOP marker was only intended for parsing in * This class and should be removed */ private boolean tempSop; /** Flag indicating whether EPH marker was only intended for parsing in * This class and should be removed */ private boolean tempEph; /** The number of tiles in the image */ private int nt; /** The number of packets per tile-part */ private int pptp; /** The name of the outfile */ private File file; /** The length of a SOT plus a SOD marker */ private static int TP_HEAD_LEN = 14; /** The maximum number of a tile part index (TPsot) */ private static int MAX_TPSOT = 16; /** The maximum number of tile parts in any tile */ private int maxtp; /** The number of packets per tile */ private int[] ppt = new int[nt]; /** The positions of the SOT, SOP and EPH markers */ private Integer[] positions; /** The main header */ private byte[] mainHeader; /** Buffers containing the tile parts */ private byte[][][] tileParts; /** Buffers containing the original tile headers */ private byte[][] tileHeaders; /** Buffers contaning the packet headers */ private byte[][][] packetHeaders; /** Buffers containing the packet data */ private byte[][][] packetData; /** Buffers containing the SOP marker segments */ private byte[][][] sopMarkSeg; /** * Instantiates a codestream manipulator.. * * @param outname The name of the original outfile * * @param nt The number of tiles in the image * * @param pptp Packets per tile-part. If zero, no division into tileparts * is performed * * @param ppm Flag indicating that PPM marker is used * * @param ppt Flag indicating that PPT marker is used * * @param tempSop Flag indicating whether SOP merker should be removed * * @param tempEph Flag indicating whether EPH merker should be removed * */ public CodestreamManipulator(File file, int nt, int pptp, boolean ppm, boolean ppt, boolean tempSop, boolean tempEph) { this.file = file; this.nt=nt; this.pptp = pptp; this.ppmUsed = ppm; this.pptUsed = ppt; this.tempSop = tempSop; this.tempEph = tempEph; } /** * This method performs the actual manipulation of the codestream which is * the reparsing for tile parts and packed packet headers * * @return The number of bytes that the file has increased by * * @exception java.io.IOException If an I/O error ocurred. * */ public int doCodestreamManipulation() throws IOException{ int addedHeaderBytes=0; ppt = new int[nt]; tileParts = new byte[nt][][]; tileHeaders = new byte[nt][]; packetHeaders = new byte[nt][][]; packetData = new byte[nt][][]; sopMarkSeg = new byte[nt][][]; // If neither packed packet header nor tile parts are used, return 0 if( ppmUsed == false && pptUsed == false && pptp == 0) return 0; // Open file for reading and writing BEBufferedRandomAccessFile fi = new BEBufferedRandomAccessFile(file, "rw+"); addedHeaderBytes -= fi.length(); // Parse the codestream for SOT, SOP and EPH markers parseAndFind(fi); // Read and buffer the tile headers, packet headers and packet data readAndBuffer(fi); // Close file and overwrite with new file fi.close(); fi = new BEBufferedRandomAccessFile(file, "rw"); // Create tile-parts createTileParts(); // Write new codestream writeNewCodestream(fi); // Close file fi.flush(); addedHeaderBytes += fi.length(); fi.close(); return addedHeaderBytes; } /** * This method parses the codestream for SOT, SOP and EPH markers and * removes header header bits signalling SOP and EPH markers if packed * packet headers are used * * @param fi The file to parse the markers from * * @exception java.io.IOException If an I/O error ocurred. * */ private void parseAndFind(BufferedRandomAccessFile fi) throws IOException{ int length,pos,i,t,sop=0,eph=0; short marker; int halfMarker; int tileEnd; Vector markPos = new Vector(); // Find position of first SOT marker marker = (short)fi.readUnsignedShort(); // read SOC marker marker = (short)fi.readUnsignedShort(); while(marker != Markers.SOT){ pos = fi.getPos(); length = fi.readUnsignedShort(); // If SOP and EPH markers were only used for parsing in this // class remove SOP and EPH markers from Scod field if(marker == Markers.COD){ int scod = fi.readUnsignedByte(); if(tempSop) scod &= 0xfd; // Remove bits indicating SOP if(tempEph) scod &= 0xfb; // Remove bits indicating SOP fi.seek(pos +2); fi.write(scod); } fi.seek(pos + length); marker = (short)fi.readUnsignedShort(); } pos = fi.getPos(); fi.seek(pos-2); // Find all packet headers, packed data and tile headers for(t=0;t<nt;t++){ // Read SOT marker fi.readUnsignedShort(); // Skip SOT pos = fi.getPos(); markPos.addElement(new Integer(fi.getPos())); fi.readInt(); // Skip Lsot and Isot length = fi.readInt(); // Read Psot fi.readUnsignedShort(); // Skip TPsot & TNsot tileEnd = pos + length-2; // Last byte of tile // Find position of SOD marker marker = (short)fi.readUnsignedShort(); while(marker != Markers.SOD){ pos = fi.getPos(); length = fi.readUnsignedShort(); // If SOP and EPH markers were only used for parsing in this // class remove SOP and EPH markers from Scod field if(marker == Markers.COD){ int scod = fi.readUnsignedByte(); if(tempSop) scod &= 0xfd; // Remove bits indicating SOP if(tempEph) scod &= 0xfb; // Remove bits indicating SOP fi.seek(pos +2); fi.write(scod); } fi.seek(pos + length); marker = (short)fi.readUnsignedShort(); } // Find all SOP and EPH markers in tile sop = 0; eph = 0; i = fi.getPos(); while(i<tileEnd){ halfMarker = (short) fi.readUnsignedByte(); if(halfMarker == (short)0xff){ marker = (short)((halfMarker<<8) + fi.readUnsignedByte()); i++; if(marker == Markers.SOP){ markPos.addElement(new Integer(fi.getPos())); ppt[t]++; sop++; fi.skipBytes(4); i+=4; } if(marker == Markers.EPH){ markPos.addElement(new Integer(fi.getPos())); eph++; } } i++; } } markPos.addElement(new Integer(fi.getPos()+2)); positions = new Integer[markPos.size()]; markPos.copyInto(positions); } /** * This method reads and buffers the tile headers, packet headers and * packet data. * * @param fi The file to read the headers and data from * * @exception java.io.IOException If an I/O error ocurred. * */ private void readAndBuffer(BufferedRandomAccessFile fi)throws IOException{ int p,prem,length,t,markIndex; // Buffer main header fi.seek(0); length = ((Integer)positions[0]).intValue()-2; mainHeader = new byte[length]; fi.readFully(mainHeader,0,length); markIndex = 0; for(t=0; t<nt; t++){ prem = ppt[t]; packetHeaders[t] = new byte[prem][]; packetData[t] = new byte[prem][]; sopMarkSeg[t] = new byte[prem][]; // Read tile header length = positions[ markIndex+1 ].intValue() - positions[ markIndex ].intValue(); tileHeaders[t] = new byte[length]; fi.readFully(tileHeaders[t],0,length); markIndex++; for(p=0; p<prem; p++){ // Read packet header length = positions[ markIndex+1 ].intValue() - positions[ markIndex ].intValue(); if(tempSop){ // SOP marker is skipped length -= Markers.SOP_LENGTH; fi.skipBytes(Markers.SOP_LENGTH); } else{ // SOP marker is read and buffered length -= Markers.SOP_LENGTH; sopMarkSeg[t][p] = new byte[Markers.SOP_LENGTH]; fi.readFully(sopMarkSeg[t][p],0,Markers.SOP_LENGTH); } if(!tempEph){ // EPH marker is kept in header length += Markers.EPH_LENGTH; } packetHeaders[t][p] = new byte[length]; fi.readFully(packetHeaders[t][p],0,length); markIndex++; // Read packet data length = positions[ markIndex+1 ].intValue() - positions[ markIndex ].intValue(); length -= Markers.EPH_LENGTH; if(tempEph){ // EPH marker is used and is skipped fi.skipBytes(Markers.EPH_LENGTH); } packetData[t][p] = new byte[length]; fi.readFully(packetData[t][p],0,length); markIndex++; } } } /** * This method creates the tileparts from the buffered tile headers, * packet headers and packet data * * @exception java.io.IOException If an I/O error ocurred. * */ private void createTileParts() throws IOException{ int i,prem,t,length; int pIndex,phIndex; int tppStart; int tilePart; int p,np, nomnp; int numTileParts; int numPackets; ByteArrayOutputStream temp = new ByteArrayOutputStream(); byte[] tempByteArr; // Create tile parts tileParts = new byte[nt][][]; maxtp = 0; for(t=0; t<nt; t++){ // Calculate number of tile parts. If tileparts are not used, // put all packets in the first tilepart if(pptp == 0) pptp = ppt[t]; prem = ppt[t]; numTileParts = (int)Math.ceil(((double)prem)/pptp); numPackets = packetHeaders[t].length; maxtp = (numTileParts>maxtp) ? numTileParts:maxtp; tileParts[t] = new byte[numTileParts][]; // Create all the tile parts for tile t tppStart = 0; pIndex = 0; p=0; phIndex = 0; for(tilePart=0;tilePart<numTileParts;tilePart++){ // Calculate number of packets in this tilepart nomnp = (pptp > prem) ? prem:pptp; np = nomnp; // Write tile part header if(tilePart == 0){ // Write original tile part header up to SOD marker temp.write(tileHeaders[t],0, tileHeaders[t].length-2); }else{ // Write empty header of length TP_HEAD_LEN-2 temp.write(new byte[TP_HEAD_LEN-2],0,TP_HEAD_LEN-2); } // Write PPT marker segments if PPT used if(pptUsed){ int pptLength = 3; // Zppt and Lppt int pptIndex = 0; int phLength; p = pIndex; while(np > 0){ phLength = packetHeaders[t][p].length; // If the total legth of the packet headers is greater // than MAX_LPPT, several PPT markers are needed if(pptLength + phLength > Markers.MAX_LPPT){ temp.write(Markers.PPT>>>8); temp.write(Markers.PPT); temp.write(pptLength>>>8); temp.write(pptLength); temp.write(pptIndex++); for(i=pIndex; i < p ; i++){ temp.write(packetHeaders[t][i],0, packetHeaders[t][i].length); } pptLength = 3; // Zppt and Lppt pIndex = p; } pptLength += phLength; p++; np--; } // Write last PPT marker temp.write(Markers.PPT>>>8); temp.write(Markers.PPT); temp.write(pptLength>>>8); temp.write(pptLength); temp.write(pptIndex); for(i=pIndex; i < p ; i++){ temp.write(packetHeaders[t][i],0, packetHeaders[t][i].length); } } pIndex = p; np = nomnp; // Write SOD marker temp.write(Markers.SOD>>>8); temp.write(Markers.SOD); // Write packet data and packet headers if PPT and PPM not used for(p=tppStart; p<tppStart+np ; p++){ if(!tempSop){ temp.write(sopMarkSeg[t][p],0,Markers.SOP_LENGTH); } if(!(ppmUsed || pptUsed)){ temp.write(packetHeaders[t][p],0, packetHeaders[t][p].length); } temp.write(packetData[t][p], 0, packetData[t][p].length); } tppStart += np; // Edit tile part header tempByteArr = temp.toByteArray(); tileParts[t][tilePart] = tempByteArr; length = temp.size(); if(tilePart == 0){ // Edit first tile part header tempByteArr[6] = (byte)(length>>>24); // Psot tempByteArr[7] = (byte)(length>>>16); tempByteArr[8] = (byte)(length>>>8); tempByteArr[9] = (byte)(length); tempByteArr[10] = (byte)(0); // TPsot tempByteArr[11] = (byte)(numTileParts); // TNsot }else{ // Edit tile part header tempByteArr[0] = (byte)(Markers.SOT>>>8); // SOT tempByteArr[1] = (byte)(Markers.SOT); tempByteArr[2] = (byte)(0); // Lsot tempByteArr[3] = (byte)(10); tempByteArr[4] = (byte)(t >> 8); // Lsot tempByteArr[5] = (byte)(t); // Isot tempByteArr[6] = (byte)(length>>>24); // Psot tempByteArr[7] = (byte)(length>>>16); tempByteArr[8] = (byte)(length>>>8); tempByteArr[9] = (byte)(length); tempByteArr[10] = (byte)(tilePart); //TPsot tempByteArr[11] = (byte)(numTileParts); // TNsot } temp.reset(); prem -= np; } } temp.close(); } /** * This method writes the new codestream to the file. * * @param fi The file to write the new codestream to * * @exception java.io.IOException If an I/O error ocurred. * */ private void writeNewCodestream(BufferedRandomAccessFile fi) throws IOException{ int i,t,p,tp; int numTiles = tileParts.length; int[][] packetHeaderLengths = new int[numTiles][maxtp]; byte[] temp; int length; // Write main header up to SOT marker fi.write(mainHeader,0,mainHeader.length); // If PPM used write all packet headers in PPM markers if(ppmUsed){ ByteArrayOutputStream ppmMarkerSegment = new ByteArrayOutputStream(); int numPackets; int totNumPackets; int ppmIndex=0; int ppmLength; int pStart,pStop; int prem[] = new int[numTiles]; // Set number of remaining packets for(t=0 ; t<numTiles ; t++) prem[t] = packetHeaders[t].length; // Calculate Nppm values for(tp=0; tp<maxtp ; tp++){ for(t=0 ; t<numTiles ; t++){ if(tileParts[t].length > tp){ totNumPackets = packetHeaders[t].length; // Calculate number of packets in this tilepart numPackets = (tp == tileParts[t].length-1) ? prem[t] : pptp; pStart = totNumPackets - prem[t]; pStop = pStart + numPackets; // Calculate number of packet header bytes for this // tile part for(p=pStart ; p < pStop ; p++) packetHeaderLengths[t][tp] += packetHeaders[t][p].length; prem[t] -= numPackets; } } } // Write first PPM marker ppmMarkerSegment.write(Markers.PPM >>> 8); ppmMarkerSegment.write(Markers.PPM); ppmMarkerSegment.write(0); // Temporary Lppm value ppmMarkerSegment.write(0); // Temporary Lppm value ppmMarkerSegment.write(0); // zppm ppmLength = 3; ppmIndex++; // Set number of remaining packets for(t=0 ; t<numTiles ; t++) prem[t] = packetHeaders[t].length; // Write all PPM markers and information for(tp=0; tp<maxtp ; tp++){ for(t=0 ; t<numTiles ; t++){ if(tileParts[t].length > tp){ totNumPackets = packetHeaders[t].length; // Calculate number of packets in this tilepart numPackets = (tp == tileParts[t].length-1) ? prem[t] : pptp; pStart = totNumPackets - prem[t]; pStop = pStart + numPackets; // If Nppm value wont fit in current PPM marker segment // write current PPM marker segment and start new if(ppmLength + 4 > Markers.MAX_LPPM){ // Write current PPM marker temp = ppmMarkerSegment.toByteArray(); length = temp.length-2; temp[2] = (byte)(length >>> 8); temp[3] = (byte)length; fi.write(temp,0,length+2); // Start new PPM marker segment ppmMarkerSegment.reset(); ppmMarkerSegment.write(Markers.PPM >>> 8); ppmMarkerSegment.write(Markers.PPM); ppmMarkerSegment.write(0); // Temporary Lppm value ppmMarkerSegment.write(0); // Temporary Lppm value ppmMarkerSegment.write(ppmIndex++); // zppm ppmLength = 3; } // Write Nppm value length = packetHeaderLengths[t][tp]; ppmMarkerSegment.write(length>>>24); ppmMarkerSegment.write(length>>>16); ppmMarkerSegment.write(length>>>8); ppmMarkerSegment.write(length); ppmLength += 4; // Write packet headers for(p=pStart ; p < pStop ; p++){ length = packetHeaders[t][p].length; // If next packet header value wont fit in // current PPM marker segment write current PPM // marker segment and start new if(ppmLength + length > Markers.MAX_LPPM){ // Write current PPM marker temp = ppmMarkerSegment.toByteArray(); length = temp.length-2; temp[2] = (byte)(length >>> 8); temp[3] = (byte)length; fi.write(temp,0,length+2); // Start new PPM marker segment ppmMarkerSegment.reset(); ppmMarkerSegment.write(Markers.PPM >>> 8); ppmMarkerSegment.write(Markers.PPM); ppmMarkerSegment.write(0); // Temp Lppm value ppmMarkerSegment.write(0); // Temp Lppm value ppmMarkerSegment.write(ppmIndex++); // zppm ppmLength = 3; } // write packet header ppmMarkerSegment.write(packetHeaders[t][p],0, packetHeaders[t][p].length); ppmLength+=packetHeaders[t][p].length; } prem[t]-=numPackets; } } } // Write last PPM marker segment temp = ppmMarkerSegment.toByteArray(); length = temp.length-2; temp[2] = (byte)(length >>> 8); temp[3] = (byte)length; fi.write(temp,0,length+2); } // Write tile parts interleaved for(tp=0;tp<maxtp;tp++) for(t=0 ; t<nt ;t++){ if(tileParts[t].length >= tp){ temp = tileParts[t][tp]; length = temp.length; fi.write(temp,0,length); } } fi.writeShort(Markers.EOC); } }