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 * */
044package jj2000.j2k.util;
045
046import jj2000.j2k.codestream.*;
047import jj2000.j2k.io.*;
048
049import java.util.*;
050import 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 * */
056public 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, boolean ppm,
135                                 boolean ppt, boolean tempSop,
136                                 boolean tempEph) {
137        this.file = file;
138        this.nt=nt;
139        this.pptp = pptp;
140        this.ppmUsed = ppm;
141        this.pptUsed = ppt;
142        this.tempSop = tempSop;
143        this.tempEph = tempEph;
144    }
145
146    /**
147     * This method performs the actual manipulation of the codestream which is
148     * the reparsing for tile parts and packed packet headers
149     *
150     * @return The number of bytes that the file has increased by
151     *
152     * @exception java.io.IOException If an I/O error ocurred.
153     * */
154    public int doCodestreamManipulation() throws IOException{
155        int addedHeaderBytes=0;
156        ppt = new int[nt];
157        tileParts     = new byte[nt][][];
158        tileHeaders   = new byte[nt][];
159        packetHeaders = new byte[nt][][];
160        packetData    = new byte[nt][][];
161        sopMarkSeg    = new byte[nt][][];
162
163        // If neither packed packet header nor tile parts are used, return 0
164        if( ppmUsed == false && pptUsed == false && pptp == 0)
165            return 0;
166
167        // Open file for reading and writing
168        BEBufferedRandomAccessFile fi =
169            new BEBufferedRandomAccessFile(file, "rw+");
170        addedHeaderBytes -= fi.length();
171
172        // Parse the codestream for SOT, SOP and EPH markers
173        parseAndFind(fi);
174
175        // Read and buffer the tile headers, packet headers and packet data
176        readAndBuffer(fi);
177
178        // Close file and overwrite with new file
179        fi.close();
180        fi = new  BEBufferedRandomAccessFile(file, "rw");
181
182        // Create tile-parts
183        createTileParts();
184
185        // Write new codestream
186        writeNewCodestream(fi);
187
188        // Close file
189        fi.flush();
190        addedHeaderBytes += fi.length();
191        fi.close();
192
193        return addedHeaderBytes;
194    }
195
196    /**
197     * This method parses the codestream for SOT, SOP and EPH markers and
198     * removes header header bits signalling SOP and EPH markers if packed
199     * packet headers are used
200     *
201     * @param fi The file to parse the markers from
202     *
203     * @exception java.io.IOException If an I/O error ocurred.
204     * */
205    private void parseAndFind(BufferedRandomAccessFile fi) 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) +
278                                        fi.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)throws IOException{
310        int p,prem,length,t,markIndex;
311
312        // Buffer main header
313        fi.seek(0);
314        length = ((Integer)positions[0]).intValue()-2;
315        mainHeader = new byte[length];
316        fi.readFully(mainHeader,0,length);
317        markIndex = 0;
318
319        for(t=0; t<nt; t++){
320            prem = ppt[t];
321
322            packetHeaders[t] = new byte[prem][];
323            packetData[t] = new byte[prem][];
324            sopMarkSeg[t] = new byte[prem][];
325
326            // Read tile header
327            length = positions[ markIndex+1 ].intValue() -
328                positions[ markIndex ].intValue();
329            tileHeaders[t] = new byte[length];
330            fi.readFully(tileHeaders[t],0,length);
331            markIndex++;
332
333            for(p=0; p<prem; p++){
334                // Read packet header
335                length = positions[ markIndex+1 ].intValue() -
336                    positions[ markIndex ].intValue();
337
338                if(tempSop){ // SOP marker is skipped
339                    length -= Markers.SOP_LENGTH;
340                    fi.skipBytes(Markers.SOP_LENGTH);
341                }
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,Markers.SOP_LENGTH);
346                }
347
348                if(!tempEph){ // EPH marker is kept in header
349                    length += Markers.EPH_LENGTH;
350                }
351
352                packetHeaders[t][p] = new byte[length];
353                fi.readFully(packetHeaders[t][p],0,length);
354                markIndex++;
355
356                // Read packet data
357                length = positions[ markIndex+1 ].intValue() -
358                    positions[ markIndex ].intValue();
359
360                length -= Markers.EPH_LENGTH;
361                 if(tempEph){ // EPH marker is used and is skipped
362                    fi.skipBytes(Markers.EPH_LENGTH);
363                }
364
365                packetData[t][p] = new byte[length];
366                fi.readFully(packetData[t][p],0,length);
367                markIndex++;
368            }
369        }
370    }
371
372    /**
373     * This method creates the tileparts from the buffered tile headers,
374     * packet headers and packet data
375     *
376     * @exception java.io.IOException If an I/O error ocurred.
377     * */
378    private void createTileParts() throws IOException{
379        int i,prem,t,length;
380        int pIndex,phIndex;
381        int tppStart;
382        int tilePart;
383        int p,np, nomnp;
384        int numTileParts;
385        int numPackets;
386        ByteArrayOutputStream temp = new ByteArrayOutputStream();
387        byte[] tempByteArr;
388
389        // Create tile parts
390        tileParts = new byte[nt][][];
391        maxtp = 0;
392
393        for(t=0; t<nt; t++){
394            // Calculate number of tile parts. If tileparts are not used,
395            // put all packets in the first tilepart
396            if(pptp == 0)
397                pptp = ppt[t];
398            prem = ppt[t];
399            numTileParts = (int)Math.ceil(((double)prem)/pptp);
400            numPackets = packetHeaders[t].length;
401            maxtp = (numTileParts>maxtp) ? numTileParts:maxtp;
402            tileParts[t] = new byte[numTileParts][];
403
404            // Create all the tile parts for tile t
405            tppStart = 0;
406            pIndex = 0;
407            p=0;
408            phIndex = 0;
409            for(tilePart=0;tilePart<numTileParts;tilePart++){
410
411                // Calculate number of packets in this tilepart
412                nomnp = (pptp > prem) ? prem:pptp;
413                np = nomnp;
414
415                // Write tile part header
416                if(tilePart == 0){
417                    // Write original tile part header up to SOD marker
418                    temp.write(tileHeaders[t],0,
419                               tileHeaders[t].length-2);
420                }else{
421                    // Write empty header of length TP_HEAD_LEN-2
422                    temp.write(new byte[TP_HEAD_LEN-2],0,TP_HEAD_LEN-2);
423                }
424
425                // Write PPT marker segments if PPT used
426                if(pptUsed){
427                    int pptLength = 3; // Zppt and Lppt
428                    int pptIndex = 0;
429                    int phLength;
430
431                    p = pIndex;
432                    while(np > 0){
433                        phLength = packetHeaders[t][p].length;
434
435                        // If the total legth of the packet headers is greater
436                        // than MAX_LPPT, several PPT markers are needed
437                        if(pptLength + phLength > Markers.MAX_LPPT){
438                            temp.write(Markers.PPT>>>8);
439                            temp.write(Markers.PPT);
440                            temp.write(pptLength>>>8);
441                            temp.write(pptLength);
442                            temp.write(pptIndex++);
443                            for(i=pIndex; i < p ; i++){
444                                temp.write(packetHeaders[t][i],0,
445                                           packetHeaders[t][i].length);
446                            }
447                            pptLength = 3; // Zppt and Lppt
448                            pIndex = p;
449                        }
450                        pptLength += phLength;
451                        p++;
452                        np--;
453                    }
454                    // Write last PPT marker
455                    temp.write(Markers.PPT>>>8);
456                    temp.write(Markers.PPT);
457                    temp.write(pptLength>>>8);
458                    temp.write(pptLength);
459                    temp.write(pptIndex);
460                    for(i=pIndex; i < p ; i++){
461
462                        temp.write(packetHeaders[t][i],0,
463                                   packetHeaders[t][i].length);
464                    }
465                }
466                pIndex = p;
467                np = nomnp;
468
469                // Write SOD marker
470                temp.write(Markers.SOD>>>8);
471                temp.write(Markers.SOD);
472
473                // Write packet data and packet headers if PPT and PPM not used
474                for(p=tppStart; p<tppStart+np ; p++){
475                  if(!tempSop){
476                        temp.write(sopMarkSeg[t][p],0,Markers.SOP_LENGTH);
477                  }
478
479                    if(!(ppmUsed || pptUsed)){
480                        temp.write(packetHeaders[t][p],0,
481                                   packetHeaders[t][p].length);
482                    }
483
484                    temp.write(packetData[t][p], 0, packetData[t][p].length);
485                }
486                tppStart += np;
487
488                // Edit tile part header
489                tempByteArr = temp.toByteArray();
490                tileParts[t][tilePart] = tempByteArr;
491                length = temp.size();
492
493                if(tilePart == 0){
494                    // Edit first tile part header
495                    tempByteArr[6] = (byte)(length>>>24);         // Psot
496                    tempByteArr[7] = (byte)(length>>>16);
497                    tempByteArr[8] = (byte)(length>>>8);
498                    tempByteArr[9] = (byte)(length);
499                    tempByteArr[10] = (byte)(0);                  // TPsot
500                    tempByteArr[11] = (byte)(numTileParts);       // TNsot
501                }else{
502                    // Edit tile part header
503                    tempByteArr[0] = (byte)(Markers.SOT>>>8);     // SOT
504                    tempByteArr[1] = (byte)(Markers.SOT);
505                    tempByteArr[2] = (byte)(0);                   // Lsot
506                    tempByteArr[3] = (byte)(10);
507                    tempByteArr[4] = (byte)(t >> 8);                   // Lsot
508                    tempByteArr[5] = (byte)(t);                   // Isot
509                    tempByteArr[6] = (byte)(length>>>24);         // Psot
510                    tempByteArr[7] = (byte)(length>>>16);
511                    tempByteArr[8] = (byte)(length>>>8);
512                    tempByteArr[9] = (byte)(length);
513                    tempByteArr[10] = (byte)(tilePart);           //TPsot
514                    tempByteArr[11] = (byte)(numTileParts);       // TNsot
515                }
516                temp.reset();
517                prem -= np;
518            }
519        }
520        temp.close();
521    }
522
523    /**
524     * This method writes the new codestream to the file.
525     *
526     * @param fi The file to write the new codestream to
527     *
528     * @exception java.io.IOException If an I/O error ocurred.
529     * */
530    private void writeNewCodestream(BufferedRandomAccessFile fi)
531        throws IOException{
532        int i,t,p,tp;
533        int numTiles = tileParts.length;
534        int[][] packetHeaderLengths = new int[numTiles][maxtp];
535        byte[] temp;
536        int length;
537
538        // Write main header up to SOT marker
539        fi.write(mainHeader,0,mainHeader.length);
540
541        // If PPM used write all packet headers in PPM markers
542        if(ppmUsed){
543            ByteArrayOutputStream ppmMarkerSegment =
544                new ByteArrayOutputStream();
545            int numPackets;
546            int totNumPackets;
547            int ppmIndex=0;
548            int ppmLength;
549            int pStart,pStop;
550            int prem[] = new int[numTiles];
551
552            // Set number of remaining packets
553            for(t=0 ; t<numTiles ; t++)
554                prem[t] = packetHeaders[t].length;
555
556            // Calculate Nppm values
557            for(tp=0; tp<maxtp ; tp++){
558                for(t=0 ; t<numTiles ; t++){
559
560                    if(tileParts[t].length > tp){
561                        totNumPackets = packetHeaders[t].length;
562                        // Calculate number of packets in this tilepart
563                        numPackets =
564                            (tp == tileParts[t].length-1) ?
565                            prem[t] : pptp;
566
567                        pStart = totNumPackets - prem[t];
568                        pStop = pStart + numPackets;
569
570                        // Calculate number of packet header bytes for this
571                        // tile part
572                        for(p=pStart ; p < pStop ; p++)
573                            packetHeaderLengths[t][tp] +=
574                                packetHeaders[t][p].length;
575
576                        prem[t] -= numPackets;
577                    }
578                }
579            }
580
581            // Write first PPM marker
582            ppmMarkerSegment.write(Markers.PPM >>> 8);
583            ppmMarkerSegment.write(Markers.PPM);
584            ppmMarkerSegment.write(0); // Temporary Lppm value
585            ppmMarkerSegment.write(0); // Temporary Lppm value
586            ppmMarkerSegment.write(0); // zppm
587            ppmLength = 3;
588            ppmIndex++;
589
590            // Set number of remaining packets
591            for(t=0 ; t<numTiles ; t++)
592                prem[t] = packetHeaders[t].length;
593
594            // Write all PPM markers and information
595            for(tp=0; tp<maxtp ; tp++){
596                for(t=0 ; t<numTiles ; t++){
597
598                    if(tileParts[t].length > tp){
599                        totNumPackets = packetHeaders[t].length;
600
601                        // Calculate number of packets in this tilepart
602                        numPackets =
603                            (tp == tileParts[t].length-1) ? prem[t] : pptp;
604
605                        pStart = totNumPackets - prem[t];
606                        pStop = pStart + numPackets;
607
608                        // If Nppm value wont fit in current PPM marker segment
609                        // write current PPM marker segment and start new
610                        if(ppmLength + 4 > Markers.MAX_LPPM){
611                            // Write current PPM marker
612                            temp = ppmMarkerSegment.toByteArray();
613                            length = temp.length-2;
614                            temp[2] = (byte)(length >>> 8);
615                            temp[3] = (byte)length;
616                            fi.write(temp,0,length+2);
617
618                            // Start new PPM marker segment
619                            ppmMarkerSegment.reset();
620                            ppmMarkerSegment.write(Markers.PPM >>> 8);
621                            ppmMarkerSegment.write(Markers.PPM);
622                            ppmMarkerSegment.write(0); // Temporary Lppm value
623                            ppmMarkerSegment.write(0); // Temporary Lppm value
624                            ppmMarkerSegment.write(ppmIndex++); // zppm
625                            ppmLength = 3;
626                        }
627
628                        // Write Nppm value
629                        length = packetHeaderLengths[t][tp];
630                        ppmMarkerSegment.write(length>>>24);
631                        ppmMarkerSegment.write(length>>>16);
632                        ppmMarkerSegment.write(length>>>8);
633                        ppmMarkerSegment.write(length);
634                        ppmLength += 4;
635
636                        // Write packet headers
637                        for(p=pStart ; p < pStop ; p++){
638                            length = packetHeaders[t][p].length;
639
640                            // If next packet header value wont fit in
641                            // current PPM marker segment write current PPM
642                            // marker segment and start new
643                            if(ppmLength + length > Markers.MAX_LPPM){
644                                // Write current PPM marker
645                                temp = ppmMarkerSegment.toByteArray();
646                                length = temp.length-2;
647                                temp[2] = (byte)(length >>> 8);
648                                temp[3] = (byte)length;
649                                fi.write(temp,0,length+2);
650
651                                // Start new PPM marker segment
652                                ppmMarkerSegment.reset();
653                                ppmMarkerSegment.write(Markers.PPM >>> 8);
654                                ppmMarkerSegment.write(Markers.PPM);
655                                ppmMarkerSegment.write(0); // Temp Lppm value
656                                ppmMarkerSegment.write(0); // Temp Lppm value
657                                ppmMarkerSegment.write(ppmIndex++); // zppm
658                                ppmLength = 3;
659                            }
660
661                            // write packet header
662                            ppmMarkerSegment.write(packetHeaders[t][p],0,
663                                                   packetHeaders[t][p].length);
664                            ppmLength+=packetHeaders[t][p].length;
665                        }
666                        prem[t]-=numPackets;
667                    }
668                }
669            }
670            // Write last PPM marker segment
671            temp = ppmMarkerSegment.toByteArray();
672            length = temp.length-2;
673            temp[2] = (byte)(length >>> 8);
674            temp[3] = (byte)length;
675            fi.write(temp,0,length+2);
676        }
677
678        // Write tile parts interleaved
679        for(tp=0;tp<maxtp;tp++)
680            for(t=0 ; t<nt ;t++){
681                if(tileParts[t].length >= tp){
682                    temp = tileParts[t][tp];
683                    length = temp.length;
684                    fi.write(temp,0,length);
685                }
686            }
687        fi.writeShort(Markers.EOC);
688    }
689}
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710