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