001: /*
002: * $RCSfile: CodestreamManipulator.java,v $
003: * $Revision: 1.1 $
004: * $Date: 2005/02/11 05:02:24 $
005: * $State: Exp $
006: *
007: * Class: CodestreamManipulator
008: *
009: * Description: Manipulates codestream to create tile-parts etc
010: *
011: *
012: *
013: * COPYRIGHT:
014: *
015: * This software module was originally developed by Raphaël Grosbois and
016: * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
017: * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
018: * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
019: * Centre France S.A) in the course of development of the JPEG2000
020: * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
021: * software module is an implementation of a part of the JPEG 2000
022: * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
023: * Systems AB and Canon Research Centre France S.A (collectively JJ2000
024: * Partners) agree not to assert against ISO/IEC and users of the JPEG
025: * 2000 Standard (Users) any of their rights under the copyright, not
026: * including other intellectual property rights, for this software module
027: * with respect to the usage by ISO/IEC and Users of this software module
028: * or modifications thereof for use in hardware or software products
029: * claiming conformance to the JPEG 2000 Standard. Those intending to use
030: * this software module in hardware or software products are advised that
031: * their use may infringe existing patents. The original developers of
032: * this software module, JJ2000 Partners and ISO/IEC assume no liability
033: * for use of this software module or modifications thereof. No license
034: * or right to this software module is granted for non JPEG 2000 Standard
035: * conforming products. JJ2000 Partners have full right to use this
036: * software module for his/her own purpose, assign or donate this
037: * software module to any third party and to inhibit third parties from
038: * using this software module for non JPEG 2000 Standard conforming
039: * products. This copyright notice must be included in all copies or
040: * derivative works of this software module.
041: *
042: * Copyright (c) 1999/2000 JJ2000 Partners.
043: * */
044: package jj2000.j2k.util;
045:
046: import jj2000.j2k.codestream.*;
047: import jj2000.j2k.io.*;
048:
049: import java.util.*;
050: import java.io.*;
051:
052: /**
053: * This class takes a legal JPEG 2000 codestream and performs some
054: * manipulation on it. Currently the manipulations supported are: Tile-parts
055: * */
056: public class CodestreamManipulator {
057:
058: /** Flag indicating whether packed packet headers in main header is used
059: * */
060: private boolean ppmUsed;
061:
062: /** Flag indicating whether packed packet headers in tile headers is used
063: * */
064: private boolean pptUsed;
065:
066: /** Flag indicating whether SOP marker was only intended for parsing in
067: * This class and should be removed */
068: private boolean tempSop;
069:
070: /** Flag indicating whether EPH marker was only intended for parsing in
071: * This class and should be removed */
072: private boolean tempEph;
073:
074: /** The number of tiles in the image */
075: private int nt;
076:
077: /** The number of packets per tile-part */
078: private int pptp;
079:
080: /** The name of the outfile */
081: private File file;
082:
083: /** The length of a SOT plus a SOD marker */
084: private static int TP_HEAD_LEN = 14;
085:
086: /** The maximum number of a tile part index (TPsot) */
087: private static int MAX_TPSOT = 16;
088:
089: /** The maximum number of tile parts in any tile */
090: private int maxtp;
091:
092: /** The number of packets per tile */
093: private int[] ppt = new int[nt];
094:
095: /** The positions of the SOT, SOP and EPH markers */
096: private Integer[] positions;
097:
098: /** The main header */
099: private byte[] mainHeader;
100:
101: /** Buffers containing the tile parts */
102: private byte[][][] tileParts;
103:
104: /** Buffers containing the original tile headers */
105: private byte[][] tileHeaders;
106:
107: /** Buffers contaning the packet headers */
108: private byte[][][] packetHeaders;
109:
110: /** Buffers containing the packet data */
111: private byte[][][] packetData;
112:
113: /** Buffers containing the SOP marker segments */
114: private byte[][][] sopMarkSeg;
115:
116: /**
117: * Instantiates a codestream manipulator..
118: *
119: * @param outname The name of the original outfile
120: *
121: * @param nt The number of tiles in the image
122: *
123: * @param pptp Packets per tile-part. If zero, no division into tileparts
124: * is performed
125: *
126: * @param ppm Flag indicating that PPM marker is used
127: *
128: * @param ppt Flag indicating that PPT marker is used
129: *
130: * @param tempSop Flag indicating whether SOP merker should be removed
131: *
132: * @param tempEph Flag indicating whether EPH merker should be removed
133: * */
134: public CodestreamManipulator(File file, int nt, int pptp,
135: boolean ppm, boolean ppt, boolean tempSop, boolean tempEph) {
136: this .file = file;
137: this .nt = nt;
138: this .pptp = pptp;
139: this .ppmUsed = ppm;
140: this .pptUsed = ppt;
141: this .tempSop = tempSop;
142: this .tempEph = tempEph;
143: }
144:
145: /**
146: * This method performs the actual manipulation of the codestream which is
147: * the reparsing for tile parts and packed packet headers
148: *
149: * @return The number of bytes that the file has increased by
150: *
151: * @exception java.io.IOException If an I/O error ocurred.
152: * */
153: public int doCodestreamManipulation() throws IOException {
154: int addedHeaderBytes = 0;
155: ppt = new int[nt];
156: tileParts = new byte[nt][][];
157: tileHeaders = new byte[nt][];
158: packetHeaders = new byte[nt][][];
159: packetData = new byte[nt][][];
160: sopMarkSeg = new byte[nt][][];
161:
162: // If neither packed packet header nor tile parts are used, return 0
163: if (ppmUsed == false && pptUsed == false && pptp == 0)
164: return 0;
165:
166: // Open file for reading and writing
167: BEBufferedRandomAccessFile fi = new BEBufferedRandomAccessFile(
168: file, "rw+");
169: addedHeaderBytes -= fi.length();
170:
171: // Parse the codestream for SOT, SOP and EPH markers
172: parseAndFind(fi);
173:
174: // Read and buffer the tile headers, packet headers and packet data
175: readAndBuffer(fi);
176:
177: // Close file and overwrite with new file
178: fi.close();
179: fi = new BEBufferedRandomAccessFile(file, "rw");
180:
181: // Create tile-parts
182: createTileParts();
183:
184: // Write new codestream
185: writeNewCodestream(fi);
186:
187: // Close file
188: fi.flush();
189: addedHeaderBytes += fi.length();
190: fi.close();
191:
192: return addedHeaderBytes;
193: }
194:
195: /**
196: * This method parses the codestream for SOT, SOP and EPH markers and
197: * removes header header bits signalling SOP and EPH markers if packed
198: * packet headers are used
199: *
200: * @param fi The file to parse the markers from
201: *
202: * @exception java.io.IOException If an I/O error ocurred.
203: * */
204: private void parseAndFind(BufferedRandomAccessFile fi)
205: throws IOException {
206: int length, pos, i, t, sop = 0, eph = 0;
207: short marker;
208: int halfMarker;
209: int tileEnd;
210: Vector markPos = new Vector();
211:
212: // Find position of first SOT marker
213: marker = (short) fi.readUnsignedShort(); // read SOC marker
214: marker = (short) fi.readUnsignedShort();
215: while (marker != Markers.SOT) {
216: pos = fi.getPos();
217: length = fi.readUnsignedShort();
218:
219: // If SOP and EPH markers were only used for parsing in this
220: // class remove SOP and EPH markers from Scod field
221: if (marker == Markers.COD) {
222: int scod = fi.readUnsignedByte();
223: if (tempSop)
224: scod &= 0xfd; // Remove bits indicating SOP
225: if (tempEph)
226: scod &= 0xfb; // Remove bits indicating SOP
227: fi.seek(pos + 2);
228: fi.write(scod);
229: }
230:
231: fi.seek(pos + length);
232: marker = (short) fi.readUnsignedShort();
233: }
234: pos = fi.getPos();
235: fi.seek(pos - 2);
236:
237: // Find all packet headers, packed data and tile headers
238: for (t = 0; t < nt; t++) {
239: // Read SOT marker
240: fi.readUnsignedShort(); // Skip SOT
241: pos = fi.getPos();
242: markPos.addElement(new Integer(fi.getPos()));
243: fi.readInt(); // Skip Lsot and Isot
244: length = fi.readInt(); // Read Psot
245: fi.readUnsignedShort(); // Skip TPsot & TNsot
246: tileEnd = pos + length - 2; // Last byte of tile
247:
248: // Find position of SOD marker
249: marker = (short) fi.readUnsignedShort();
250: while (marker != Markers.SOD) {
251: pos = fi.getPos();
252: length = fi.readUnsignedShort();
253:
254: // If SOP and EPH markers were only used for parsing in this
255: // class remove SOP and EPH markers from Scod field
256: if (marker == Markers.COD) {
257: int scod = fi.readUnsignedByte();
258: if (tempSop)
259: scod &= 0xfd; // Remove bits indicating SOP
260: if (tempEph)
261: scod &= 0xfb; // Remove bits indicating SOP
262: fi.seek(pos + 2);
263: fi.write(scod);
264: }
265: fi.seek(pos + length);
266: marker = (short) fi.readUnsignedShort();
267: }
268:
269: // Find all SOP and EPH markers in tile
270: sop = 0;
271: eph = 0;
272:
273: i = fi.getPos();
274: while (i < tileEnd) {
275: halfMarker = (short) fi.readUnsignedByte();
276: if (halfMarker == (short) 0xff) {
277: marker = (short) ((halfMarker << 8) + fi
278: .readUnsignedByte());
279: i++;
280: if (marker == Markers.SOP) {
281: markPos.addElement(new Integer(fi.getPos()));
282: ppt[t]++;
283: sop++;
284: fi.skipBytes(4);
285: i += 4;
286: }
287:
288: if (marker == Markers.EPH) {
289: markPos.addElement(new Integer(fi.getPos()));
290: eph++;
291: }
292: }
293: i++;
294: }
295: }
296: markPos.addElement(new Integer(fi.getPos() + 2));
297: positions = new Integer[markPos.size()];
298: markPos.copyInto(positions);
299: }
300:
301: /**
302: * This method reads and buffers the tile headers, packet headers and
303: * packet data.
304: *
305: * @param fi The file to read the headers and data from
306: *
307: * @exception java.io.IOException If an I/O error ocurred.
308: * */
309: private void readAndBuffer(BufferedRandomAccessFile fi)
310: throws IOException {
311: int p, prem, length, t, markIndex;
312:
313: // Buffer main header
314: fi.seek(0);
315: length = ((Integer) positions[0]).intValue() - 2;
316: mainHeader = new byte[length];
317: fi.readFully(mainHeader, 0, length);
318: markIndex = 0;
319:
320: for (t = 0; t < nt; t++) {
321: prem = ppt[t];
322:
323: packetHeaders[t] = new byte[prem][];
324: packetData[t] = new byte[prem][];
325: sopMarkSeg[t] = new byte[prem][];
326:
327: // Read tile header
328: length = positions[markIndex + 1].intValue()
329: - positions[markIndex].intValue();
330: tileHeaders[t] = new byte[length];
331: fi.readFully(tileHeaders[t], 0, length);
332: markIndex++;
333:
334: for (p = 0; p < prem; p++) {
335: // Read packet header
336: length = positions[markIndex + 1].intValue()
337: - positions[markIndex].intValue();
338:
339: if (tempSop) { // SOP marker is skipped
340: length -= Markers.SOP_LENGTH;
341: fi.skipBytes(Markers.SOP_LENGTH);
342: } else { // SOP marker is read and buffered
343: length -= Markers.SOP_LENGTH;
344: sopMarkSeg[t][p] = new byte[Markers.SOP_LENGTH];
345: fi.readFully(sopMarkSeg[t][p], 0,
346: Markers.SOP_LENGTH);
347: }
348:
349: if (!tempEph) { // EPH marker is kept in header
350: length += Markers.EPH_LENGTH;
351: }
352:
353: packetHeaders[t][p] = new byte[length];
354: fi.readFully(packetHeaders[t][p], 0, length);
355: markIndex++;
356:
357: // Read packet data
358: length = positions[markIndex + 1].intValue()
359: - positions[markIndex].intValue();
360:
361: length -= Markers.EPH_LENGTH;
362: if (tempEph) { // EPH marker is used and is skipped
363: fi.skipBytes(Markers.EPH_LENGTH);
364: }
365:
366: packetData[t][p] = new byte[length];
367: fi.readFully(packetData[t][p], 0, length);
368: markIndex++;
369: }
370: }
371: }
372:
373: /**
374: * This method creates the tileparts from the buffered tile headers,
375: * packet headers and packet data
376: *
377: * @exception java.io.IOException If an I/O error ocurred.
378: * */
379: private void createTileParts() throws IOException {
380: int i, prem, t, length;
381: int pIndex, phIndex;
382: int tppStart;
383: int tilePart;
384: int p, np, nomnp;
385: int numTileParts;
386: int numPackets;
387: ByteArrayOutputStream temp = new ByteArrayOutputStream();
388: byte[] tempByteArr;
389:
390: // Create tile parts
391: tileParts = new byte[nt][][];
392: maxtp = 0;
393:
394: for (t = 0; t < nt; t++) {
395: // Calculate number of tile parts. If tileparts are not used,
396: // put all packets in the first tilepart
397: if (pptp == 0)
398: pptp = ppt[t];
399: prem = ppt[t];
400: numTileParts = (int) Math.ceil(((double) prem) / pptp);
401: numPackets = packetHeaders[t].length;
402: maxtp = (numTileParts > maxtp) ? numTileParts : maxtp;
403: tileParts[t] = new byte[numTileParts][];
404:
405: // Create all the tile parts for tile t
406: tppStart = 0;
407: pIndex = 0;
408: p = 0;
409: phIndex = 0;
410: for (tilePart = 0; tilePart < numTileParts; tilePart++) {
411:
412: // Calculate number of packets in this tilepart
413: nomnp = (pptp > prem) ? prem : pptp;
414: np = nomnp;
415:
416: // Write tile part header
417: if (tilePart == 0) {
418: // Write original tile part header up to SOD marker
419: temp.write(tileHeaders[t], 0,
420: tileHeaders[t].length - 2);
421: } else {
422: // Write empty header of length TP_HEAD_LEN-2
423: temp.write(new byte[TP_HEAD_LEN - 2], 0,
424: TP_HEAD_LEN - 2);
425: }
426:
427: // Write PPT marker segments if PPT used
428: if (pptUsed) {
429: int pptLength = 3; // Zppt and Lppt
430: int pptIndex = 0;
431: int phLength;
432:
433: p = pIndex;
434: while (np > 0) {
435: phLength = packetHeaders[t][p].length;
436:
437: // If the total legth of the packet headers is greater
438: // than MAX_LPPT, several PPT markers are needed
439: if (pptLength + phLength > Markers.MAX_LPPT) {
440: temp.write(Markers.PPT >>> 8);
441: temp.write(Markers.PPT);
442: temp.write(pptLength >>> 8);
443: temp.write(pptLength);
444: temp.write(pptIndex++);
445: for (i = pIndex; i < p; i++) {
446: temp.write(packetHeaders[t][i], 0,
447: packetHeaders[t][i].length);
448: }
449: pptLength = 3; // Zppt and Lppt
450: pIndex = p;
451: }
452: pptLength += phLength;
453: p++;
454: np--;
455: }
456: // Write last PPT marker
457: temp.write(Markers.PPT >>> 8);
458: temp.write(Markers.PPT);
459: temp.write(pptLength >>> 8);
460: temp.write(pptLength);
461: temp.write(pptIndex);
462: for (i = pIndex; i < p; i++) {
463:
464: temp.write(packetHeaders[t][i], 0,
465: packetHeaders[t][i].length);
466: }
467: }
468: pIndex = p;
469: np = nomnp;
470:
471: // Write SOD marker
472: temp.write(Markers.SOD >>> 8);
473: temp.write(Markers.SOD);
474:
475: // Write packet data and packet headers if PPT and PPM not used
476: for (p = tppStart; p < tppStart + np; p++) {
477: if (!tempSop) {
478: temp.write(sopMarkSeg[t][p], 0,
479: Markers.SOP_LENGTH);
480: }
481:
482: if (!(ppmUsed || pptUsed)) {
483: temp.write(packetHeaders[t][p], 0,
484: packetHeaders[t][p].length);
485: }
486:
487: temp.write(packetData[t][p], 0,
488: packetData[t][p].length);
489: }
490: tppStart += np;
491:
492: // Edit tile part header
493: tempByteArr = temp.toByteArray();
494: tileParts[t][tilePart] = tempByteArr;
495: length = temp.size();
496:
497: if (tilePart == 0) {
498: // Edit first tile part header
499: tempByteArr[6] = (byte) (length >>> 24); // Psot
500: tempByteArr[7] = (byte) (length >>> 16);
501: tempByteArr[8] = (byte) (length >>> 8);
502: tempByteArr[9] = (byte) (length);
503: tempByteArr[10] = (byte) (0); // TPsot
504: tempByteArr[11] = (byte) (numTileParts); // TNsot
505: } else {
506: // Edit tile part header
507: tempByteArr[0] = (byte) (Markers.SOT >>> 8); // SOT
508: tempByteArr[1] = (byte) (Markers.SOT);
509: tempByteArr[2] = (byte) (0); // Lsot
510: tempByteArr[3] = (byte) (10);
511: tempByteArr[4] = (byte) (t >> 8); // Lsot
512: tempByteArr[5] = (byte) (t); // Isot
513: tempByteArr[6] = (byte) (length >>> 24); // Psot
514: tempByteArr[7] = (byte) (length >>> 16);
515: tempByteArr[8] = (byte) (length >>> 8);
516: tempByteArr[9] = (byte) (length);
517: tempByteArr[10] = (byte) (tilePart); //TPsot
518: tempByteArr[11] = (byte) (numTileParts); // TNsot
519: }
520: temp.reset();
521: prem -= np;
522: }
523: }
524: temp.close();
525: }
526:
527: /**
528: * This method writes the new codestream to the file.
529: *
530: * @param fi The file to write the new codestream to
531: *
532: * @exception java.io.IOException If an I/O error ocurred.
533: * */
534: private void writeNewCodestream(BufferedRandomAccessFile fi)
535: throws IOException {
536: int i, t, p, tp;
537: int numTiles = tileParts.length;
538: int[][] packetHeaderLengths = new int[numTiles][maxtp];
539: byte[] temp;
540: int length;
541:
542: // Write main header up to SOT marker
543: fi.write(mainHeader, 0, mainHeader.length);
544:
545: // If PPM used write all packet headers in PPM markers
546: if (ppmUsed) {
547: ByteArrayOutputStream ppmMarkerSegment = new ByteArrayOutputStream();
548: int numPackets;
549: int totNumPackets;
550: int ppmIndex = 0;
551: int ppmLength;
552: int pStart, pStop;
553: int prem[] = new int[numTiles];
554:
555: // Set number of remaining packets
556: for (t = 0; t < numTiles; t++)
557: prem[t] = packetHeaders[t].length;
558:
559: // Calculate Nppm values
560: for (tp = 0; tp < maxtp; tp++) {
561: for (t = 0; t < numTiles; t++) {
562:
563: if (tileParts[t].length > tp) {
564: totNumPackets = packetHeaders[t].length;
565: // Calculate number of packets in this tilepart
566: numPackets = (tp == tileParts[t].length - 1) ? prem[t]
567: : pptp;
568:
569: pStart = totNumPackets - prem[t];
570: pStop = pStart + numPackets;
571:
572: // Calculate number of packet header bytes for this
573: // tile part
574: for (p = pStart; p < pStop; p++)
575: packetHeaderLengths[t][tp] += packetHeaders[t][p].length;
576:
577: prem[t] -= numPackets;
578: }
579: }
580: }
581:
582: // Write first PPM marker
583: ppmMarkerSegment.write(Markers.PPM >>> 8);
584: ppmMarkerSegment.write(Markers.PPM);
585: ppmMarkerSegment.write(0); // Temporary Lppm value
586: ppmMarkerSegment.write(0); // Temporary Lppm value
587: ppmMarkerSegment.write(0); // zppm
588: ppmLength = 3;
589: ppmIndex++;
590:
591: // Set number of remaining packets
592: for (t = 0; t < numTiles; t++)
593: prem[t] = packetHeaders[t].length;
594:
595: // Write all PPM markers and information
596: for (tp = 0; tp < maxtp; tp++) {
597: for (t = 0; t < numTiles; t++) {
598:
599: if (tileParts[t].length > tp) {
600: totNumPackets = packetHeaders[t].length;
601:
602: // Calculate number of packets in this tilepart
603: numPackets = (tp == tileParts[t].length - 1) ? prem[t]
604: : pptp;
605:
606: pStart = totNumPackets - prem[t];
607: pStop = pStart + numPackets;
608:
609: // If Nppm value wont fit in current PPM marker segment
610: // write current PPM marker segment and start new
611: if (ppmLength + 4 > Markers.MAX_LPPM) {
612: // Write current PPM marker
613: temp = ppmMarkerSegment.toByteArray();
614: length = temp.length - 2;
615: temp[2] = (byte) (length >>> 8);
616: temp[3] = (byte) length;
617: fi.write(temp, 0, length + 2);
618:
619: // Start new PPM marker segment
620: ppmMarkerSegment.reset();
621: ppmMarkerSegment.write(Markers.PPM >>> 8);
622: ppmMarkerSegment.write(Markers.PPM);
623: ppmMarkerSegment.write(0); // Temporary Lppm value
624: ppmMarkerSegment.write(0); // Temporary Lppm value
625: ppmMarkerSegment.write(ppmIndex++); // zppm
626: ppmLength = 3;
627: }
628:
629: // Write Nppm value
630: length = packetHeaderLengths[t][tp];
631: ppmMarkerSegment.write(length >>> 24);
632: ppmMarkerSegment.write(length >>> 16);
633: ppmMarkerSegment.write(length >>> 8);
634: ppmMarkerSegment.write(length);
635: ppmLength += 4;
636:
637: // Write packet headers
638: for (p = pStart; p < pStop; p++) {
639: length = packetHeaders[t][p].length;
640:
641: // If next packet header value wont fit in
642: // current PPM marker segment write current PPM
643: // marker segment and start new
644: if (ppmLength + length > Markers.MAX_LPPM) {
645: // Write current PPM marker
646: temp = ppmMarkerSegment.toByteArray();
647: length = temp.length - 2;
648: temp[2] = (byte) (length >>> 8);
649: temp[3] = (byte) length;
650: fi.write(temp, 0, length + 2);
651:
652: // Start new PPM marker segment
653: ppmMarkerSegment.reset();
654: ppmMarkerSegment
655: .write(Markers.PPM >>> 8);
656: ppmMarkerSegment.write(Markers.PPM);
657: ppmMarkerSegment.write(0); // Temp Lppm value
658: ppmMarkerSegment.write(0); // Temp Lppm value
659: ppmMarkerSegment.write(ppmIndex++); // zppm
660: ppmLength = 3;
661: }
662:
663: // write packet header
664: ppmMarkerSegment.write(packetHeaders[t][p],
665: 0, packetHeaders[t][p].length);
666: ppmLength += packetHeaders[t][p].length;
667: }
668: prem[t] -= numPackets;
669: }
670: }
671: }
672: // Write last PPM marker segment
673: temp = ppmMarkerSegment.toByteArray();
674: length = temp.length - 2;
675: temp[2] = (byte) (length >>> 8);
676: temp[3] = (byte) length;
677: fi.write(temp, 0, length + 2);
678: }
679:
680: // Write tile parts interleaved
681: for (tp = 0; tp < maxtp; tp++)
682: for (t = 0; t < nt; t++) {
683: if (tileParts[t].length >= tp) {
684: temp = tileParts[t][tp];
685: length = temp.length;
686: fi.write(temp, 0, length);
687: }
688: }
689: fi.writeShort(Markers.EOC);
690: }
691: }
|