/* * CVS identifier: * * $Id: CodestreamManipulator.java,v 1.17 2001/05/16 13:58:09 qtxjoas 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. * */ using System; using CSJ2K.j2k.codestream; using CSJ2K.j2k.io; namespace CSJ2K.j2k.util { /// This class takes a legal JPEG 2000 codestream and performs some /// manipulation on it. Currently the manipulations supported are: Tile-parts /// /// public class CodestreamManipulator { private void InitBlock() { ppt = new int[nt]; } /// Flag indicating whether packed packet headers in main header is used /// /// private bool ppmUsed; /// Flag indicating whether packed packet headers in tile headers is used /// /// private bool pptUsed; /// Flag indicating whether SOP marker was only intended for parsing in /// This class and should be removed /// private bool tempSop; /// Flag indicating whether EPH marker was only intended for parsing in /// This class and should be removed /// private bool 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 System.String outname; /// 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 //UPGRADE_NOTE: The initialization of 'ppt' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'" private int[] ppt; /// The positions of the SOT, SOP and EPH markers private System.Int32[] 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.. /// /// /// The name of the original outfile /// /// /// The number of tiles in the image /// /// /// Packets per tile-part. If zero, no division into tileparts /// is performed /// /// /// Flag indicating that PPM marker is used /// /// /// Flag indicating that PPT marker is used /// /// /// Flag indicating whether SOP merker should be removed /// /// /// Flag indicating whether EPH merker should be removed /// /// public CodestreamManipulator(System.String outname, int nt, int pptp, bool ppm, bool ppt, bool tempSop, bool tempEph) { InitBlock(); this.outname = outname; 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 /// /// /// The number of bytes that the file has increased by /// /// /// If an I/O error ocurred. /// /// public virtual int doCodestreamManipulation() { BEBufferedRandomAccessFile fi; 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 fi = new BEBufferedRandomAccessFile(outname, "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(outname, "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 /// /// /// The file to parse the markers from /// /// /// If an I/O error ocurred. /// /// private void parseAndFind(BufferedRandomAccessFile fi) { int length, pos, i, t, sop = 0, eph = 0; short marker; int halfMarker; int tileEnd; System.Collections.ArrayList markPos = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // Find position of first SOT marker marker = (short) fi.readUnsignedShort(); // read SOC marker marker = (short) fi.readUnsignedShort(); while (marker != CSJ2K.j2k.codestream.Markers.SOT) { pos = fi.Pos; 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 == CSJ2K.j2k.codestream.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.Pos; 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.Pos; markPos.Add((System.Int32) fi.Pos); 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 != CSJ2K.j2k.codestream.Markers.SOD) { pos = fi.Pos; 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 == CSJ2K.j2k.codestream.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.Pos; while (i < tileEnd) { halfMarker = (short) fi.readUnsignedByte(); if (halfMarker == (short) 0xff) { marker = (short) ((halfMarker << 8) + fi.readUnsignedByte()); i++; if (marker == CSJ2K.j2k.codestream.Markers.SOP) { markPos.Add((System.Int32) fi.Pos); ppt[t]++; sop++; fi.skipBytes(4); i += 4; } if (marker == CSJ2K.j2k.codestream.Markers.EPH) { markPos.Add((System.Int32) fi.Pos); eph++; } } i++; } } markPos.Add((System.Int32) (fi.Pos + 2)); positions = new System.Int32[markPos.Count]; markPos.CopyTo(positions); } /// This method reads and buffers the tile headers, packet headers and /// packet data. /// /// /// The file to read the headers and data from /// /// /// If an I/O error ocurred. /// /// private void readAndBuffer(BufferedRandomAccessFile fi) { int p, prem, length, t, markIndex; // Buffer main header fi.seek(0); length = ((System.Int32) positions[0]) - 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] - positions[markIndex]; 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] - positions[markIndex]; if (tempSop) { // SOP marker is skipped length -= CSJ2K.j2k.codestream.Markers.SOP_LENGTH; fi.skipBytes(CSJ2K.j2k.codestream.Markers.SOP_LENGTH); } else { // SOP marker is read and buffered length -= CSJ2K.j2k.codestream.Markers.SOP_LENGTH; sopMarkSeg[t][p] = new byte[CSJ2K.j2k.codestream.Markers.SOP_LENGTH]; fi.readFully(sopMarkSeg[t][p], 0, CSJ2K.j2k.codestream.Markers.SOP_LENGTH); } if (!tempEph) { // EPH marker is kept in header length += CSJ2K.j2k.codestream.Markers.EPH_LENGTH; } packetHeaders[t][p] = new byte[length]; fi.readFully(packetHeaders[t][p], 0, length); markIndex++; // Read packet data length = positions[markIndex + 1] - positions[markIndex]; length -= CSJ2K.j2k.codestream.Markers.EPH_LENGTH; if (tempEph) { // EPH marker is used and is skipped fi.skipBytes(CSJ2K.j2k.codestream.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 /// /// /// If an I/O error ocurred. /// /// private void createTileParts() { int i, prem, t, length; int pIndex; // phIndex removed int tppStart; int tilePart; int p, np, nomnp; int numTileParts; int numPackets; System.IO.MemoryStream temp = new System.IO.MemoryStream(); 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]; //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" numTileParts = (int) System.Math.Ceiling(((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 > CSJ2K.j2k.codestream.Markers.MAX_LPPT) { temp.WriteByte((System.Byte) SupportClass.URShift(CSJ2K.j2k.codestream.Markers.PPT, 8)); temp.WriteByte((System.Byte) (CSJ2K.j2k.codestream.Markers.PPT & 0x00FF)); temp.WriteByte((System.Byte) SupportClass.URShift(pptLength, 8)); temp.WriteByte((System.Byte) pptLength); temp.WriteByte((System.Byte) 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.WriteByte((System.Byte) SupportClass.URShift(CSJ2K.j2k.codestream.Markers.PPT, 8)); temp.WriteByte((System.Byte) (CSJ2K.j2k.codestream.Markers.PPT & 0x00FF)); temp.WriteByte((System.Byte) SupportClass.URShift(pptLength, 8)); temp.WriteByte((System.Byte) pptLength); temp.WriteByte((System.Byte) 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.WriteByte((System.Byte) SupportClass.URShift(CSJ2K.j2k.codestream.Markers.SOD, 8)); temp.WriteByte((System.Byte) (CSJ2K.j2k.codestream.Markers.SOD & 0x00FF)); // 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, CSJ2K.j2k.codestream.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.ToArray(); tileParts[t][tilePart] = tempByteArr; length = (int)temp.Length; if (tilePart == 0) { // Edit first tile part header tempByteArr[6] = (byte) (SupportClass.URShift(length, 24)); // Psot tempByteArr[7] = (byte) (SupportClass.URShift(length, 16)); tempByteArr[8] = (byte) (SupportClass.URShift(length, 8)); tempByteArr[9] = (byte) (length); tempByteArr[10] = (byte) SupportClass.Identity((0)); // TPsot tempByteArr[11] = (byte) (numTileParts); // TNsot } else { // Edit tile part header tempByteArr[0] = (byte) (SupportClass.URShift(CSJ2K.j2k.codestream.Markers.SOT, 8)); // SOT tempByteArr[1] = (byte) (CSJ2K.j2k.codestream.Markers.SOT & 0x00FF); tempByteArr[2] = (byte) SupportClass.Identity((0)); // Lsot tempByteArr[3] = (byte) SupportClass.Identity((10)); tempByteArr[4] = (byte) (SupportClass.URShift(t, 8)); // Isot tempByteArr[5] = (byte) (t); // tempByteArr[6] = (byte) (SupportClass.URShift(length, 24)); // Psot tempByteArr[7] = (byte) (SupportClass.URShift(length, 16)); tempByteArr[8] = (byte) (SupportClass.URShift(length, 8)); tempByteArr[9] = (byte) (length); tempByteArr[10] = (byte) (tilePart); //TPsot tempByteArr[11] = (byte) (numTileParts); // TNsot } //UPGRADE_ISSUE: Method 'java.io.ByteArrayOutputStream.reset' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioByteArrayOutputStreamreset'" //temp.reset(); temp.SetLength(0); prem -= np; } } temp.Close(); } /// This method writes the new codestream to the file. /// /// /// The file to write the new codestream to /// /// /// If an I/O error ocurred. /// /// private void writeNewCodestream(BufferedRandomAccessFile fi) { int t, p, tp; // i removed int numTiles = tileParts.Length; int[][] packetHeaderLengths = new int[numTiles][]; for (int i2 = 0; i2 < numTiles; i2++) { packetHeaderLengths[i2] = new int[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) { System.IO.MemoryStream ppmMarkerSegment = new System.IO.MemoryStream(); 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.WriteByte((System.Byte) SupportClass.URShift(CSJ2K.j2k.codestream.Markers.PPM, 8)); ppmMarkerSegment.WriteByte((System.Byte) (CSJ2K.j2k.codestream.Markers.PPM & 0x00FF)); ppmMarkerSegment.WriteByte((System.Byte) 0); // Temporary Lppm value ppmMarkerSegment.WriteByte((System.Byte) 0); // Temporary Lppm value ppmMarkerSegment.WriteByte((System.Byte) 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 > CSJ2K.j2k.codestream.Markers.MAX_LPPM) { // Write current PPM marker temp = ppmMarkerSegment.ToArray(); length = temp.Length - 2; temp[2] = (byte) (SupportClass.URShift(length, 8)); temp[3] = (byte) length; fi.write(temp, 0, length + 2); // Start new PPM marker segment //UPGRADE_ISSUE: Method 'java.io.ByteArrayOutputStream.reset' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioByteArrayOutputStreamreset'" //ppmMarkerSegment.reset(); ppmMarkerSegment.SetLength(0); ppmMarkerSegment.WriteByte((System.Byte) SupportClass.URShift(CSJ2K.j2k.codestream.Markers.PPM, 8)); ppmMarkerSegment.WriteByte((System.Byte) (CSJ2K.j2k.codestream.Markers.PPM & 0x00FF)); ppmMarkerSegment.WriteByte((System.Byte) 0); // Temporary Lppm value ppmMarkerSegment.WriteByte((System.Byte) 0); // Temporary Lppm value ppmMarkerSegment.WriteByte((System.Byte) ppmIndex++); // zppm ppmLength = 3; } // Write Nppm value length = packetHeaderLengths[t][tp]; ppmMarkerSegment.WriteByte((System.Byte) SupportClass.URShift(length, 24)); ppmMarkerSegment.WriteByte((System.Byte) SupportClass.URShift(length, 16)); ppmMarkerSegment.WriteByte((System.Byte) SupportClass.URShift(length, 8)); ppmMarkerSegment.WriteByte((System.Byte) 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 > CSJ2K.j2k.codestream.Markers.MAX_LPPM) { // Write current PPM marker temp = ppmMarkerSegment.ToArray(); length = temp.Length - 2; temp[2] = (byte) (SupportClass.URShift(length, 8)); temp[3] = (byte) length; fi.write(temp, 0, length + 2); // Start new PPM marker segment //UPGRADE_ISSUE: Method 'java.io.ByteArrayOutputStream.reset' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioByteArrayOutputStreamreset'" //ppmMarkerSegment.reset(); ppmMarkerSegment.SetLength(0); ppmMarkerSegment.WriteByte((System.Byte) SupportClass.URShift(CSJ2K.j2k.codestream.Markers.PPM, 8)); ppmMarkerSegment.WriteByte((System.Byte) (CSJ2K.j2k.codestream.Markers.PPM & 0x00FF)); ppmMarkerSegment.WriteByte((System.Byte) 0); // Temp Lppm value ppmMarkerSegment.WriteByte((System.Byte) 0); // Temp Lppm value ppmMarkerSegment.WriteByte((System.Byte) 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.ToArray(); length = temp.Length - 2; temp[2] = (byte) (SupportClass.URShift(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(CSJ2K.j2k.codestream.Markers.EOC); } } }