1 7 8 package com.sun.imageio.plugins.jpeg; 9 10 import javax.imageio.ImageTypeSpecifier ; 11 import javax.imageio.ImageWriteParam ; 12 import javax.imageio.IIOException ; 13 import javax.imageio.stream.ImageInputStream ; 14 import javax.imageio.stream.ImageOutputStream ; 15 import javax.imageio.metadata.IIOMetadata ; 16 import javax.imageio.metadata.IIOMetadataNode ; 17 import javax.imageio.metadata.IIOMetadataFormat ; 18 import javax.imageio.metadata.IIOMetadataFormatImpl ; 19 import javax.imageio.metadata.IIOInvalidTreeException ; 20 import javax.imageio.plugins.jpeg.JPEGQTable ; 21 import javax.imageio.plugins.jpeg.JPEGHuffmanTable ; 22 import javax.imageio.plugins.jpeg.JPEGImageWriteParam ; 23 24 import org.w3c.dom.Node ; 25 import org.w3c.dom.NodeList ; 26 import org.w3c.dom.NamedNodeMap ; 27 28 import java.util.List ; 29 import java.util.ArrayList ; 30 import java.util.Arrays ; 31 import java.util.Iterator ; 32 import java.util.ListIterator ; 33 import java.io.IOException ; 34 import java.awt.color.ICC_Profile ; 35 import java.awt.color.ICC_ColorSpace ; 36 import java.awt.color.ColorSpace ; 37 import java.awt.image.ColorModel ; 38 import java.awt.Point ; 39 40 43 public class JPEGMetadata extends IIOMetadata implements Cloneable { 44 45 47 private static final boolean debug = false; 48 49 54 private List resetSequence = null; 55 56 62 private boolean inThumb = false; 63 64 70 private boolean hasAlpha; 71 72 74 76 85 List markerSequence = new ArrayList (); 86 87 91 final boolean isStream; 92 93 95 97 100 JPEGMetadata(boolean isStream, boolean inThumb) { 101 super(true, JPEG.nativeImageMetadataFormatName, JPEG.nativeImageMetadataFormatClassName, 104 null, null); this.inThumb = inThumb; 106 this.isStream = isStream; 108 if (isStream) { 109 nativeMetadataFormatName = JPEG.nativeStreamMetadataFormatName; 110 nativeMetadataFormatClassName = 111 JPEG.nativeStreamMetadataFormatClassName; 112 } 113 } 114 115 129 JPEGMetadata(boolean isStream, 130 boolean isThumb, 131 ImageInputStream iis, 132 JPEGImageReader reader) throws IOException { 133 this(isStream, isThumb); 134 135 JPEGBuffer buffer = new JPEGBuffer(iis); 136 137 buffer.loadBuf(0); 138 139 if (((buffer.buf[0] & 0xff) != 0xff) 141 || ((buffer.buf[1] & 0xff) != JPEG.SOI) 142 || ((buffer.buf[2] & 0xff) != 0xff)) { 143 throw new IIOException ("Image format error"); 144 } 145 146 boolean done = false; 147 buffer.bufAvail -=2; buffer.bufPtr = 2; 149 MarkerSegment newGuy = null; 150 while (!done) { 151 byte [] buf; 152 int ptr; 153 buffer.loadBuf(1); 154 if (debug) { 155 System.out.println("top of loop"); 156 buffer.print(10); 157 } 158 buffer.scanForFF(reader); 159 switch (buffer.buf[buffer.bufPtr] & 0xff) { 160 case 0: 161 if (debug) { 162 System.out.println("Skipping 0"); 163 } 164 buffer.bufAvail--; 165 buffer.bufPtr++; 166 break; 167 case JPEG.SOF0: 168 case JPEG.SOF1: 169 case JPEG.SOF2: 170 if (isStream) { 171 throw new IIOException 172 ("SOF not permitted in stream metadata"); 173 } 174 newGuy = new SOFMarkerSegment(buffer); 175 break; 176 case JPEG.DQT: 177 newGuy = new DQTMarkerSegment(buffer); 178 break; 179 case JPEG.DHT: 180 newGuy = new DHTMarkerSegment(buffer); 181 break; 182 case JPEG.DRI: 183 newGuy = new DRIMarkerSegment(buffer); 184 break; 185 case JPEG.APP0: 186 buffer.loadBuf(8); buf = buffer.buf; 189 ptr = buffer.bufPtr; 190 if ((buf[ptr+3] == 'J') 191 && (buf[ptr+4] == 'F') 192 && (buf[ptr+5] == 'I') 193 && (buf[ptr+6] == 'F') 194 && (buf[ptr+7] == 0)) { 195 if (inThumb) { 196 reader.warningOccurred 197 (JPEGImageReader.WARNING_NO_JFIF_IN_THUMB); 198 JFIFMarkerSegment dummy = 201 new JFIFMarkerSegment(buffer); 202 } else if (isStream) { 203 throw new IIOException 204 ("JFIF not permitted in stream metadata"); 205 } else if (markerSequence.isEmpty() == false) { 206 throw new IIOException 207 ("JFIF APP0 must be first marker after SOI"); 208 } else { 209 newGuy = new JFIFMarkerSegment(buffer); 210 } 211 } else if ((buf[ptr+3] == 'J') 212 && (buf[ptr+4] == 'F') 213 && (buf[ptr+5] == 'X') 214 && (buf[ptr+6] == 'X') 215 && (buf[ptr+7] == 0)) { 216 if (isStream) { 217 throw new IIOException 218 ("JFXX not permitted in stream metadata"); 219 } 220 if (inThumb) { 221 throw new IIOException 222 ("JFXX markers not allowed in JFIF JPEG thumbnail"); 223 } 224 JFIFMarkerSegment jfif = 225 (JFIFMarkerSegment) findMarkerSegment 226 (JFIFMarkerSegment.class, true); 227 if (jfif == null) { 228 throw new IIOException 229 ("JFXX encountered without prior JFIF!"); 230 } 231 jfif.addJFXX(buffer, reader); 232 } else { 234 newGuy = new MarkerSegment(buffer); 235 newGuy.loadData(buffer); 236 } 237 break; 238 case JPEG.APP2: 239 buffer.loadBuf(15); if ((buffer.buf[buffer.bufPtr+3] == 'I') 242 && (buffer.buf[buffer.bufPtr+4] == 'C') 243 && (buffer.buf[buffer.bufPtr+5] == 'C') 244 && (buffer.buf[buffer.bufPtr+6] == '_') 245 && (buffer.buf[buffer.bufPtr+7] == 'P') 246 && (buffer.buf[buffer.bufPtr+8] == 'R') 247 && (buffer.buf[buffer.bufPtr+9] == 'O') 248 && (buffer.buf[buffer.bufPtr+10] == 'F') 249 && (buffer.buf[buffer.bufPtr+11] == 'I') 250 && (buffer.buf[buffer.bufPtr+12] == 'L') 251 && (buffer.buf[buffer.bufPtr+13] == 'E') 252 && (buffer.buf[buffer.bufPtr+14] == 0) 253 ) { 254 if (isStream) { 255 throw new IIOException 256 ("ICC profiles not permitted in stream metadata"); 257 } 258 259 JFIFMarkerSegment jfif = 260 (JFIFMarkerSegment) findMarkerSegment 261 (JFIFMarkerSegment.class, true); 262 if (jfif == null) { 263 throw new IIOException 264 ("ICC APP2 encountered without prior JFIF!"); 265 } 266 jfif.addICC(buffer); 267 } else { 269 newGuy = new MarkerSegment(buffer); 270 newGuy.loadData(buffer); 271 } 272 break; 273 case JPEG.APP14: 274 buffer.loadBuf(8); if ((buffer.buf[buffer.bufPtr+3] == 'A') 277 && (buffer.buf[buffer.bufPtr+4] == 'd') 278 && (buffer.buf[buffer.bufPtr+5] == 'o') 279 && (buffer.buf[buffer.bufPtr+6] == 'b') 280 && (buffer.buf[buffer.bufPtr+7] == 'e')) { 281 if (isStream) { 282 throw new IIOException 283 ("Adobe APP14 markers not permitted in stream metadata"); 284 } 285 newGuy = new AdobeMarkerSegment(buffer); 286 } else { 287 newGuy = new MarkerSegment(buffer); 288 newGuy.loadData(buffer); 289 } 290 291 break; 292 case JPEG.COM: 293 newGuy = new COMMarkerSegment(buffer); 294 break; 295 case JPEG.SOS: 296 if (isStream) { 297 throw new IIOException 298 ("SOS not permitted in stream metadata"); 299 } 300 newGuy = new SOSMarkerSegment(buffer); 301 break; 302 case JPEG.RST0: 303 case JPEG.RST1: 304 case JPEG.RST2: 305 case JPEG.RST3: 306 case JPEG.RST4: 307 case JPEG.RST5: 308 case JPEG.RST6: 309 case JPEG.RST7: 310 if (debug) { 311 System.out.println("Restart Marker"); 312 } 313 buffer.bufPtr++; buffer.bufAvail--; 315 break; 316 case JPEG.EOI: 317 done = true; 318 buffer.bufPtr++; 319 buffer.bufAvail--; 320 break; 321 default: 322 newGuy = new MarkerSegment(buffer); 323 newGuy.loadData(buffer); 324 newGuy.unknown = true; 325 break; 326 } 327 if (newGuy != null) { 328 markerSequence.add(newGuy); 329 if (debug) { 330 newGuy.print(); 331 } 332 newGuy = null; 333 } 334 } 335 336 340 buffer.pushBack(); 341 342 if (!isConsistent()) { 343 throw new IIOException ("Inconsistent metadata read from stream"); 344 } 345 } 346 347 351 JPEGMetadata(ImageWriteParam param, JPEGImageWriter writer) { 352 this(true, false); 353 354 JPEGImageWriteParam jparam = null; 355 356 if ((param != null) && (param instanceof JPEGImageWriteParam )) { 357 jparam = (JPEGImageWriteParam ) param; 358 if (!jparam.areTablesSet()) { 359 jparam = null; 360 } 361 } 362 if (jparam != null) { 363 markerSequence.add(new DQTMarkerSegment(jparam.getQTables())); 364 markerSequence.add 365 (new DHTMarkerSegment(jparam.getDCHuffmanTables(), 366 jparam.getACHuffmanTables())); 367 } else { 368 markerSequence.add(new DQTMarkerSegment(JPEG.getDefaultQTables())); 370 markerSequence.add(new DHTMarkerSegment(JPEG.getDefaultHuffmanTables(true), 371 JPEG.getDefaultHuffmanTables(false))); 372 } 373 374 if (!isConsistent()) { 376 throw new InternalError ("Default stream metadata is inconsistent"); 377 } 378 } 379 380 384 JPEGMetadata(ImageTypeSpecifier imageType, 385 ImageWriteParam param, 386 JPEGImageWriter writer) { 387 this(false, false); 388 389 boolean wantJFIF = true; 390 boolean wantAdobe = false; 391 int transform = JPEG.ADOBE_UNKNOWN; 392 boolean willSubsample = true; 393 boolean wantICC = false; 394 boolean wantProg = false; 395 boolean wantOptimized = false; 396 boolean wantExtended = false; 397 boolean wantQTables = true; 398 boolean wantHTables = true; 399 float quality = JPEG.DEFAULT_QUALITY; 400 byte[] componentIDs = { 1, 2, 3, 4}; 401 int numComponents = 0; 402 403 ImageTypeSpecifier destType = null; 404 405 if (param != null) { 406 destType = param.getDestinationType(); 407 if (destType != null) { 408 if (imageType != null) { 409 writer.warningOccurred 411 (JPEGImageWriter.WARNING_DEST_IGNORED); 412 destType = null; 413 } 414 } 415 if (param.canWriteProgressive()) { 417 if (param.getProgressiveMode() == ImageWriteParam.MODE_DEFAULT) { 420 wantProg = true; 421 wantOptimized = true; 422 wantHTables = false; 423 } 424 } 425 426 if (param instanceof JPEGImageWriteParam ) { 427 JPEGImageWriteParam jparam = (JPEGImageWriteParam ) param; 428 if (jparam.areTablesSet()) { 429 wantQTables = false; wantHTables = false; 431 if ((jparam.getDCHuffmanTables().length > 2) 432 || (jparam.getACHuffmanTables().length > 2)) { 433 wantExtended = true; 434 } 435 } 436 if (!wantProg) { 439 wantOptimized = jparam.getOptimizeHuffmanTables(); 440 if (wantOptimized) { 441 wantHTables = false; 442 } 443 } 444 } 445 446 if (param.canWriteCompressed()) { 451 if (param.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) { 452 quality = param.getCompressionQuality(); 453 } 454 } 455 } 456 457 459 ColorSpace cs = null; 460 if (destType != null) { 461 ColorModel cm = destType.getColorModel(); 462 numComponents = cm.getNumComponents(); 463 boolean hasExtraComponents = (cm.getNumColorComponents() != numComponents); 464 boolean hasAlpha = cm.hasAlpha(); 465 cs = cm.getColorSpace(); 466 int type = cs.getType(); 467 switch(type) { 468 case ColorSpace.TYPE_GRAY: 469 willSubsample = false; 470 if (hasExtraComponents) { wantJFIF = false; 472 } 473 break; 474 case ColorSpace.TYPE_3CLR: 475 if (cs == JPEG.YCC) { 476 wantJFIF = false; 477 componentIDs[0] = (byte) 'Y'; 478 componentIDs[1] = (byte) 'C'; 479 componentIDs[2] = (byte) 'c'; 480 if (hasAlpha) { 481 componentIDs[3] = (byte) 'A'; 482 } 483 } 484 break; 485 case ColorSpace.TYPE_YCbCr: 486 if (hasExtraComponents) { wantJFIF = false; 488 if (!hasAlpha) { wantAdobe = true; 490 transform = JPEG.ADOBE_YCCK; 491 } 492 } 493 break; 494 case ColorSpace.TYPE_RGB: wantJFIF = false; 496 wantAdobe = true; 497 willSubsample = false; 498 componentIDs[0] = (byte) 'R'; 499 componentIDs[1] = (byte) 'G'; 500 componentIDs[2] = (byte) 'B'; 501 if (hasAlpha) { 502 componentIDs[3] = (byte) 'A'; 503 } 504 break; 505 default: 506 wantJFIF = false; 509 willSubsample = false; 510 } 511 } else if (imageType != null) { 512 ColorModel cm = imageType.getColorModel(); 513 numComponents = cm.getNumComponents(); 514 boolean hasExtraComponents = (cm.getNumColorComponents() != numComponents); 515 boolean hasAlpha = cm.hasAlpha(); 516 cs = cm.getColorSpace(); 517 int type = cs.getType(); 518 switch(type) { 519 case ColorSpace.TYPE_GRAY: 520 willSubsample = false; 521 if (hasExtraComponents) { wantJFIF = false; 523 } 524 break; 525 case ColorSpace.TYPE_RGB: if (hasAlpha) { 528 wantJFIF = false; 529 } 530 break; 531 case ColorSpace.TYPE_3CLR: 532 wantJFIF = false; 533 willSubsample = false; 534 if (cs.equals(ColorSpace.getInstance(ColorSpace.CS_PYCC))) { 535 willSubsample = true; 536 wantAdobe = true; 537 componentIDs[0] = (byte) 'Y'; 538 componentIDs[1] = (byte) 'C'; 539 componentIDs[2] = (byte) 'c'; 540 if (hasAlpha) { 541 componentIDs[3] = (byte) 'A'; 542 } 543 } 544 break; 545 case ColorSpace.TYPE_YCbCr: 546 if (hasExtraComponents) { wantJFIF = false; 548 if (!hasAlpha) { wantAdobe = true; 550 transform = JPEG.ADOBE_YCCK; 551 } 552 } 553 break; 554 case ColorSpace.TYPE_CMYK: 555 wantJFIF = false; 556 wantAdobe = true; 557 transform = JPEG.ADOBE_YCCK; 558 break; 559 560 default: 561 wantJFIF = false; 564 willSubsample = false; 565 } 566 567 } 568 569 if (wantJFIF && JPEG.isNonStandardICC(cs)) { 571 wantICC = true; 572 } 573 574 if (wantJFIF) { 576 JFIFMarkerSegment jfif = new JFIFMarkerSegment(); 577 markerSequence.add(jfif); 578 if (wantICC) { 579 try { 580 jfif.addICC((ICC_ColorSpace )cs); 581 } catch (IOException e) {} } 583 } 584 if (wantAdobe) { 586 markerSequence.add(new AdobeMarkerSegment(transform)); 587 } 588 589 if (wantQTables) { 591 markerSequence.add(new DQTMarkerSegment(quality, willSubsample)); 592 } 593 594 if (wantHTables) { 596 markerSequence.add(new DHTMarkerSegment(willSubsample)); 597 } 598 599 markerSequence.add(new SOFMarkerSegment(wantProg, 601 wantExtended, 602 willSubsample, 603 componentIDs, 604 numComponents)); 605 606 if (!wantProg) { markerSequence.add(new SOSMarkerSegment(willSubsample, 609 componentIDs, 610 numComponents)); 611 } 612 613 if (!isConsistent()) { 615 throw new InternalError ("Default image metadata is inconsistent"); 616 } 617 } 618 619 621 624 628 MarkerSegment findMarkerSegment(int tag) { 629 Iterator iter = markerSequence.iterator(); 630 while (iter.hasNext()) { 631 MarkerSegment seg = (MarkerSegment)iter.next(); 632 if (seg.tag == tag) { 633 return seg; 634 } 635 } 636 return null; 637 } 638 639 643 MarkerSegment findMarkerSegment(Class cls, boolean first) { 644 if (first) { 645 Iterator iter = markerSequence.iterator(); 646 while (iter.hasNext()) { 647 MarkerSegment seg = (MarkerSegment)iter.next(); 648 if (cls.isInstance(seg)) { 649 return seg; 650 } 651 } 652 } else { 653 ListIterator iter = markerSequence.listIterator(markerSequence.size()); 654 while (iter.hasPrevious()) { 655 MarkerSegment seg = (MarkerSegment)iter.previous(); 656 if (cls.isInstance(seg)) { 657 return seg; 658 } 659 } 660 } 661 return null; 662 } 663 664 668 private int findMarkerSegmentPosition(Class cls, boolean first) { 669 if (first) { 670 ListIterator iter = markerSequence.listIterator(); 671 for (int i = 0; iter.hasNext(); i++) { 672 MarkerSegment seg = (MarkerSegment)iter.next(); 673 if (cls.isInstance(seg)) { 674 return i; 675 } 676 } 677 } else { 678 ListIterator iter = markerSequence.listIterator(markerSequence.size()); 679 for (int i = markerSequence.size()-1; iter.hasPrevious(); i--) { 680 MarkerSegment seg = (MarkerSegment)iter.previous(); 681 if (cls.isInstance(seg)) { 682 return i; 683 } 684 } 685 } 686 return -1; 687 } 688 689 private int findLastUnknownMarkerSegmentPosition() { 690 ListIterator iter = markerSequence.listIterator(markerSequence.size()); 691 for (int i = markerSequence.size()-1; iter.hasPrevious(); i--) { 692 MarkerSegment seg = (MarkerSegment)iter.previous(); 693 if (seg.unknown == true) { 694 return i; 695 } 696 } 697 return -1; 698 } 699 700 702 protected Object clone() { 703 JPEGMetadata newGuy = null; 704 try { 705 newGuy = (JPEGMetadata) super.clone(); 706 } catch (CloneNotSupportedException e) {} if (markerSequence != null) { 708 newGuy.markerSequence = (List ) cloneSequence(); 709 } 710 newGuy.resetSequence = null; 711 return newGuy; 712 } 713 714 717 private List cloneSequence() { 718 if (markerSequence == null) { 719 return null; 720 } 721 List retval = new ArrayList (markerSequence.size()); 722 Iterator iter = markerSequence.iterator(); 723 while(iter.hasNext()) { 724 MarkerSegment seg = (MarkerSegment)iter.next(); 725 retval.add(seg.clone()); 726 } 727 728 return retval; 729 } 730 731 732 734 public Node getAsTree(String formatName) { 735 if (formatName == null) { 736 throw new IllegalArgumentException ("null formatName!"); 737 } 738 if (isStream) { 739 if (formatName.equals(JPEG.nativeStreamMetadataFormatName)) { 740 return getNativeTree(); 741 } 742 } else { 743 if (formatName.equals(JPEG.nativeImageMetadataFormatName)) { 744 return getNativeTree(); 745 } 746 if (formatName.equals 747 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 748 return getStandardTree(); 749 } 750 } 751 throw new IllegalArgumentException ("Unsupported format name: " 752 + formatName); 753 } 754 755 IIOMetadataNode getNativeTree() { 756 IIOMetadataNode root; 757 IIOMetadataNode top; 758 Iterator iter = markerSequence.iterator(); 759 if (isStream) { 760 root = new IIOMetadataNode (JPEG.nativeStreamMetadataFormatName); 761 top = root; 762 } else { 763 IIOMetadataNode sequence = new IIOMetadataNode ("markerSequence"); 764 if (!inThumb) { 765 root = new IIOMetadataNode (JPEG.nativeImageMetadataFormatName); 766 IIOMetadataNode header = new IIOMetadataNode ("JPEGvariety"); 767 root.appendChild(header); 768 JFIFMarkerSegment jfif = (JFIFMarkerSegment) 769 findMarkerSegment(JFIFMarkerSegment.class, true); 770 if (jfif != null) { 771 iter.next(); header.appendChild(jfif.getNativeNode()); 773 } 774 root.appendChild(sequence); 775 } else { 776 root = sequence; 777 } 778 top = sequence; 779 } 780 while(iter.hasNext()) { 781 MarkerSegment seg = (MarkerSegment) iter.next(); 782 top.appendChild(seg.getNativeNode()); 783 } 784 return root; 785 } 786 787 789 protected IIOMetadataNode getStandardChromaNode() { 790 hasAlpha = false; 792 SOFMarkerSegment sof = (SOFMarkerSegment) 795 findMarkerSegment(SOFMarkerSegment.class, true); 796 if (sof == null) { 797 return null; 799 } 800 801 IIOMetadataNode chroma = new IIOMetadataNode ("Chroma"); 802 IIOMetadataNode csType = new IIOMetadataNode ("ColorSpaceType"); 803 chroma.appendChild(csType); 804 805 int numChannels = sof.componentSpecs.length; 807 808 IIOMetadataNode numChanNode = new IIOMetadataNode ("NumChannels"); 809 chroma.appendChild(numChanNode); 810 numChanNode.setAttribute("value", Integer.toString(numChannels)); 811 812 if (findMarkerSegment(JFIFMarkerSegment.class, true) != null) { 814 if (numChannels == 1) { 815 csType.setAttribute("name", "GRAY"); 816 } else { 817 csType.setAttribute("name", "YCbCr"); 818 } 819 return chroma; 820 } 821 822 AdobeMarkerSegment adobe = 824 (AdobeMarkerSegment) findMarkerSegment(AdobeMarkerSegment.class, true); 825 if (adobe != null){ 826 switch (adobe.transform) { 827 case JPEG.ADOBE_YCCK: 828 csType.setAttribute("name", "YCCK"); 829 break; 830 case JPEG.ADOBE_YCC: 831 csType.setAttribute("name", "YCbCr"); 832 break; 833 case JPEG.ADOBE_UNKNOWN: 834 if (numChannels == 3) { 835 csType.setAttribute("name", "RGB"); 836 } else if (numChannels == 4) { 837 csType.setAttribute("name", "CMYK"); 838 } 839 break; 840 } 841 return chroma; 842 } 843 844 if (numChannels < 3) { 846 csType.setAttribute("name", "GRAY"); 847 if (numChannels == 2) { 848 hasAlpha = true; 849 } 850 return chroma; 851 } 852 853 boolean idsAreJFIF = true; 854 855 for (int i = 0; i < sof.componentSpecs.length; i++) { 856 int id = sof.componentSpecs[i].componentId; 857 if ((id < 1) || (id >= sof.componentSpecs.length)) { 858 idsAreJFIF = false; 859 } 860 } 861 862 if (idsAreJFIF) { 863 csType.setAttribute("name", "YCbCr"); 864 if (numChannels == 4) { 865 hasAlpha = true; 866 } 867 return chroma; 868 } 869 870 if ((sof.componentSpecs[0].componentId == 'R') 872 && (sof.componentSpecs[1].componentId == 'G') 873 && (sof.componentSpecs[2].componentId == 'B')){ 874 875 csType.setAttribute("name", "RGB"); 876 if ((numChannels == 4) 877 && (sof.componentSpecs[3].componentId == 'A')) { 878 hasAlpha = true; 879 } 880 return chroma; 881 } 882 883 if ((sof.componentSpecs[0].componentId == 'Y') 884 && (sof.componentSpecs[1].componentId == 'C') 885 && (sof.componentSpecs[2].componentId == 'c')){ 886 887 csType.setAttribute("name", "PhotoYCC"); 888 if ((numChannels == 4) 889 && (sof.componentSpecs[3].componentId == 'A')) { 890 hasAlpha = true; 891 } 892 return chroma; 893 } 894 895 898 boolean subsampled = false; 899 900 int hfactor = sof.componentSpecs[0].HsamplingFactor; 901 int vfactor = sof.componentSpecs[0].VsamplingFactor; 902 903 for (int i = 1; i<sof.componentSpecs.length; i++) { 904 if ((sof.componentSpecs[i].HsamplingFactor != hfactor) 905 || (sof.componentSpecs[i].VsamplingFactor != vfactor)){ 906 subsampled = true; 907 break; 908 } 909 } 910 911 if (subsampled) { 912 csType.setAttribute("name", "YCbCr"); 913 if (numChannels == 4) { 914 hasAlpha = true; 915 } 916 return chroma; 917 } 918 919 if (numChannels == 3) { 921 csType.setAttribute("name", "RGB"); 922 } else { 923 csType.setAttribute("name", "CMYK"); 924 } 925 926 return chroma; 927 } 928 929 protected IIOMetadataNode getStandardCompressionNode() { 930 931 IIOMetadataNode compression = new IIOMetadataNode ("Compression"); 932 933 IIOMetadataNode name = new IIOMetadataNode ("CompressionTypeName"); 935 name.setAttribute("value", "JPEG"); 936 compression.appendChild(name); 937 938 IIOMetadataNode lossless = new IIOMetadataNode ("Lossless"); 940 lossless.setAttribute("value", "false"); 941 compression.appendChild(lossless); 942 943 int sosCount = 0; 945 Iterator iter = markerSequence.iterator(); 946 while (iter.hasNext()) { 947 MarkerSegment ms = (MarkerSegment) iter.next(); 948 if (ms.tag == JPEG.SOS) { 949 sosCount++; 950 } 951 } 952 if (sosCount != 0) { 953 IIOMetadataNode prog = new IIOMetadataNode ("NumProgressiveScans"); 954 prog.setAttribute("value", Integer.toString(sosCount)); 955 compression.appendChild(prog); 956 } 957 958 return compression; 959 } 960 961 protected IIOMetadataNode getStandardDimensionNode() { 962 IIOMetadataNode dim = new IIOMetadataNode ("Dimension"); 965 IIOMetadataNode orient = new IIOMetadataNode ("ImageOrientation"); 966 orient.setAttribute("value", "normal"); 967 dim.appendChild(orient); 968 969 JFIFMarkerSegment jfif = 970 (JFIFMarkerSegment) findMarkerSegment(JFIFMarkerSegment.class, true); 971 if (jfif != null) { 972 973 float aspectRatio; 975 if (jfif.resUnits == 0) { 976 aspectRatio = ((float) jfif.Xdensity)/jfif.Ydensity; 978 } else { 979 aspectRatio = ((float) jfif.Ydensity)/jfif.Xdensity; 981 } 982 IIOMetadataNode aspect = new IIOMetadataNode ("PixelAspectRatio"); 983 aspect.setAttribute("value", Float.toString(aspectRatio)); 984 dim.insertBefore(aspect, orient); 985 986 if (jfif.resUnits != 0) { 988 float scale = (jfif.resUnits == 1) ? 25.4F : 10.0F; 990 991 IIOMetadataNode horiz = 992 new IIOMetadataNode ("HorizontalPixelSize"); 993 horiz.setAttribute("value", 994 Float.toString(scale/jfif.Xdensity)); 995 dim.appendChild(horiz); 996 997 IIOMetadataNode vert = 998 new IIOMetadataNode ("VerticalPixelSize"); 999 vert.setAttribute("value", 1000 Float.toString(scale/jfif.Ydensity)); 1001 dim.appendChild(vert); 1002 } 1003 } 1004 return dim; 1005 } 1006 1007 protected IIOMetadataNode getStandardTextNode() { 1008 IIOMetadataNode text = null; 1009 if (findMarkerSegment(JPEG.COM) != null) { 1011 text = new IIOMetadataNode ("Text"); 1012 Iterator iter = markerSequence.iterator(); 1013 while (iter.hasNext()) { 1014 MarkerSegment seg = (MarkerSegment) iter.next(); 1015 if (seg.tag == JPEG.COM) { 1016 COMMarkerSegment com = (COMMarkerSegment) seg; 1017 IIOMetadataNode entry = new IIOMetadataNode ("TextEntry"); 1018 entry.setAttribute("keyword", "comment"); 1019 entry.setAttribute("value", com.getComment()); 1020 text.appendChild(entry); 1021 } 1022 } 1023 } 1024 return text; 1025 } 1026 1027 protected IIOMetadataNode getStandardTransparencyNode() { 1028 IIOMetadataNode trans = null; 1029 if (hasAlpha == true) { 1030 trans = new IIOMetadataNode ("Transparency"); 1031 IIOMetadataNode alpha = new IIOMetadataNode ("Alpha"); 1032 alpha.setAttribute("value", "nonpremultiplied"); trans.appendChild(alpha); 1034 } 1035 return trans; 1036 } 1037 1038 1040 public boolean isReadOnly() { 1041 return false; 1042 } 1043 1044 public void mergeTree(String formatName, Node root) 1045 throws IIOInvalidTreeException { 1046 if (formatName == null) { 1047 throw new IllegalArgumentException ("null formatName!"); 1048 } 1049 if (root == null) { 1050 throw new IllegalArgumentException ("null root!"); 1051 } 1052 List copy = null; 1053 if (resetSequence == null) { 1054 resetSequence = cloneSequence(); copy = resetSequence; } else { 1057 copy = cloneSequence(); 1058 } 1059 if (isStream && 1060 (formatName.equals(JPEG.nativeStreamMetadataFormatName))) { 1061 mergeNativeTree(root); 1062 } else if (!isStream && 1063 (formatName.equals(JPEG.nativeImageMetadataFormatName))) { 1064 mergeNativeTree(root); 1065 } else if (!isStream && 1066 (formatName.equals 1067 (IIOMetadataFormatImpl.standardMetadataFormatName))) { 1068 mergeStandardTree(root); 1069 } else { 1070 throw new IllegalArgumentException ("Unsupported format name: " 1071 + formatName); 1072 } 1073 if (!isConsistent()) { 1074 markerSequence = copy; 1075 throw new IIOInvalidTreeException 1076 ("Merged tree is invalid; original restored", root); 1077 } 1078 } 1079 1080 private void mergeNativeTree(Node root) throws IIOInvalidTreeException { 1081 String name = root.getNodeName(); 1082 if (name != ((isStream) ? JPEG.nativeStreamMetadataFormatName 1083 : JPEG.nativeImageMetadataFormatName)) { 1084 throw new IIOInvalidTreeException ("Invalid root node name: " + name, 1085 root); 1086 } 1087 if (root.getChildNodes().getLength() != 2) { throw new IIOInvalidTreeException ( 1089 "JPEGvariety and markerSequence nodes must be present", root); 1090 } 1091 mergeJFIFsubtree(root.getFirstChild()); 1092 mergeSequenceSubtree(root.getLastChild()); 1093 } 1094 1095 1102 private void mergeJFIFsubtree(Node JPEGvariety) 1103 throws IIOInvalidTreeException { 1104 if (JPEGvariety.getChildNodes().getLength() != 0) { 1105 Node jfifNode = JPEGvariety.getFirstChild(); 1106 JFIFMarkerSegment jfifSeg = 1108 (JFIFMarkerSegment) findMarkerSegment(JFIFMarkerSegment.class, true); 1109 if (jfifSeg != null) { 1110 jfifSeg.updateFromNativeNode(jfifNode, false); 1111 } else { 1112 markerSequence.add(0, new JFIFMarkerSegment(jfifNode)); 1114 } 1115 } 1116 } 1117 1118 private void mergeSequenceSubtree(Node sequenceTree) 1119 throws IIOInvalidTreeException { 1120 NodeList children = sequenceTree.getChildNodes(); 1121 for (int i = 0; i < children.getLength(); i++) { 1122 Node node = children.item(i); 1123 String name = node.getNodeName(); 1124 if (name.equals("dqt")) { 1125 mergeDQTNode(node); 1126 } else if (name.equals("dht")) { 1127 mergeDHTNode(node); 1128 } else if (name.equals("dri")) { 1129 mergeDRINode(node); 1130 } else if (name.equals("com")) { 1131 mergeCOMNode(node); 1132 } else if (name.equals("app14Adobe")) { 1133 mergeAdobeNode(node); 1134 } else if (name.equals("unknown")) { 1135 mergeUnknownNode(node); 1136 } else if (name.equals("sof")) { 1137 mergeSOFNode(node); 1138 } else if (name.equals("sos")) { 1139 mergeSOSNode(node); 1140 } else { 1141 throw new IIOInvalidTreeException ("Invalid node: " + name, node); 1142 } 1143 } 1144 } 1145 1146 1163 private void mergeDQTNode(Node node) throws IIOInvalidTreeException { 1164 ArrayList oldDQTs = new ArrayList (); 1166 Iterator iter = markerSequence.iterator(); 1167 while (iter.hasNext()) { 1168 MarkerSegment seg = (MarkerSegment) iter.next(); 1169 if (seg instanceof DQTMarkerSegment) { 1170 oldDQTs.add(seg); 1171 } 1172 } 1173 if (!oldDQTs.isEmpty()) { 1174 NodeList children = node.getChildNodes(); 1175 for (int i = 0; i < children.getLength(); i++) { 1176 Node child = children.item(i); 1177 int childID = MarkerSegment.getAttributeValue(child, 1178 null, 1179 "qtableId", 1180 0, 3, 1181 true); 1182 DQTMarkerSegment dqt = null; 1183 int tableIndex = -1; 1184 for (int j = 0; j < oldDQTs.size(); j++) { 1185 DQTMarkerSegment testDQT = (DQTMarkerSegment) oldDQTs.get(j); 1186 for (int k = 0; k < testDQT.tables.size(); k++) { 1187 DQTMarkerSegment.Qtable testTable = 1188 (DQTMarkerSegment.Qtable) testDQT.tables.get(k); 1189 if (childID == testTable.tableID) { 1190 dqt = testDQT; 1191 tableIndex = k; 1192 break; 1193 } 1194 } 1195 if (dqt != null) break; 1196 } 1197 if (dqt != null) { 1198 dqt.tables.set(tableIndex, dqt.getQtableFromNode(child)); 1199 } else { 1200 dqt = (DQTMarkerSegment) oldDQTs.get(oldDQTs.size()-1); 1201 dqt.tables.add(dqt.getQtableFromNode(child)); 1202 } 1203 } 1204 } else { 1205 DQTMarkerSegment newGuy = new DQTMarkerSegment(node); 1206 int firstDHT = findMarkerSegmentPosition(DHTMarkerSegment.class, true); 1207 int firstSOF = findMarkerSegmentPosition(SOFMarkerSegment.class, true); 1208 int firstSOS = findMarkerSegmentPosition(SOSMarkerSegment.class, true); 1209 if (firstDHT != -1) { 1210 markerSequence.add(firstDHT, newGuy); 1211 } else if (firstSOF != -1) { 1212 markerSequence.add(firstSOF, newGuy); 1213 } else if (firstSOS != -1) { 1214 markerSequence.add(firstSOS, newGuy); 1215 } else { 1216 markerSequence.add(newGuy); 1217 } 1218 } 1219 } 1220 1221 1239 private void mergeDHTNode(Node node) throws IIOInvalidTreeException { 1240 ArrayList oldDHTs = new ArrayList (); 1242 Iterator iter = markerSequence.iterator(); 1243 while (iter.hasNext()) { 1244 MarkerSegment seg = (MarkerSegment) iter.next(); 1245 if (seg instanceof DHTMarkerSegment) { 1246 oldDHTs.add(seg); 1247 } 1248 } 1249 if (!oldDHTs.isEmpty()) { 1250 NodeList children = node.getChildNodes(); 1251 for (int i = 0; i < children.getLength(); i++) { 1252 Node child = children.item(i); 1253 NamedNodeMap attrs = node.getAttributes(); 1254 int childID = MarkerSegment.getAttributeValue(child, 1255 attrs, 1256 "htableId", 1257 0, 3, 1258 true); 1259 int childClass = MarkerSegment.getAttributeValue(child, 1260 attrs, 1261 "class", 1262 0, 1, 1263 true); 1264 DHTMarkerSegment dht = null; 1265 int tableIndex = -1; 1266 for (int j = 0; j < oldDHTs.size(); j++) { 1267 DHTMarkerSegment testDHT = (DHTMarkerSegment) oldDHTs.get(j); 1268 for (int k = 0; k < testDHT.tables.size(); k++) { 1269 DHTMarkerSegment.Htable testTable = 1270 (DHTMarkerSegment.Htable) testDHT.tables.get(k); 1271 if ((childID == testTable.tableID) && 1272 (childClass == testTable.tableClass)) { 1273 dht = testDHT; 1274 tableIndex = k; 1275 break; 1276 } 1277 } 1278 if (dht != null) break; 1279 } 1280 if (dht != null) { 1281 dht.tables.set(tableIndex, dht.getHtableFromNode(child)); 1282 } else { 1283 dht = (DHTMarkerSegment) oldDHTs.get(oldDHTs.size()-1); 1284 dht.tables.add(dht.getHtableFromNode(child)); 1285 } 1286 } 1287 } else { 1288 DHTMarkerSegment newGuy = new DHTMarkerSegment(node); 1289 int lastDQT = findMarkerSegmentPosition(DQTMarkerSegment.class, false); 1290 int firstSOF = findMarkerSegmentPosition(SOFMarkerSegment.class, true); 1291 int firstSOS = findMarkerSegmentPosition(SOSMarkerSegment.class, true); 1292 if (lastDQT != -1) { 1293 markerSequence.add(lastDQT+1, newGuy); 1294 } else if (firstSOF != -1) { 1295 markerSequence.add(firstSOF, newGuy); 1296 } else if (firstSOS != -1) { 1297 markerSequence.add(firstSOS, newGuy); 1298 } else { 1299 markerSequence.add(newGuy); 1300 } 1301 } 1302 } 1303 1304 1317 private void mergeDRINode(Node node) throws IIOInvalidTreeException { 1318 DRIMarkerSegment dri = 1319 (DRIMarkerSegment) findMarkerSegment(DRIMarkerSegment.class, true); 1320 if (dri != null) { 1321 dri.updateFromNativeNode(node, false); 1322 } else { 1323 DRIMarkerSegment newGuy = new DRIMarkerSegment(node); 1324 int firstSOF = findMarkerSegmentPosition(SOFMarkerSegment.class, true); 1325 int firstSOS = findMarkerSegmentPosition(SOSMarkerSegment.class, true); 1326 if (firstSOF != -1) { 1327 markerSequence.add(firstSOF, newGuy); 1328 } else if (firstSOS != -1) { 1329 markerSequence.add(firstSOS, newGuy); 1330 } else { 1331 markerSequence.add(newGuy); 1332 } 1333 } 1334 } 1335 1336 1341 private void mergeCOMNode(Node node) throws IIOInvalidTreeException { 1342 COMMarkerSegment newGuy = new COMMarkerSegment(node); 1343 insertCOMMarkerSegment(newGuy); 1344 } 1345 1346 1358 private void insertCOMMarkerSegment(COMMarkerSegment newGuy) { 1359 int lastCOM = findMarkerSegmentPosition(COMMarkerSegment.class, false); 1360 boolean hasJFIF = (findMarkerSegment(JFIFMarkerSegment.class, true) != null); 1361 int firstAdobe = findMarkerSegmentPosition(AdobeMarkerSegment.class, true); 1362 if (lastCOM != -1) { 1363 markerSequence.add(lastCOM+1, newGuy); 1364 } else if (hasJFIF) { 1365 markerSequence.add(1, newGuy); } else if (firstAdobe != -1) { 1367 markerSequence.add(firstAdobe+1, newGuy); 1368 } else { 1369 markerSequence.add(0, newGuy); 1370 } 1371 } 1372 1373 1380 private void mergeAdobeNode(Node node) throws IIOInvalidTreeException { 1381 AdobeMarkerSegment adobe = 1382 (AdobeMarkerSegment) findMarkerSegment(AdobeMarkerSegment.class, true); 1383 if (adobe != null) { 1384 adobe.updateFromNativeNode(node, false); 1385 } else { 1386 AdobeMarkerSegment newGuy = new AdobeMarkerSegment(node); 1387 insertAdobeMarkerSegment(newGuy); 1388 } 1389 } 1390 1391 1401 private void insertAdobeMarkerSegment(AdobeMarkerSegment newGuy) { 1402 boolean hasJFIF = 1403 (findMarkerSegment(JFIFMarkerSegment.class, true) != null); 1404 int lastUnknown = findLastUnknownMarkerSegmentPosition(); 1405 if (hasJFIF) { 1406 markerSequence.add(1, newGuy); } else if (lastUnknown != -1) { 1408 markerSequence.add(lastUnknown+1, newGuy); 1409 } else { 1410 markerSequence.add(0, newGuy); 1411 } 1412 } 1413 1414 1427 private void mergeUnknownNode(Node node) throws IIOInvalidTreeException { 1428 MarkerSegment newGuy = new MarkerSegment(node); 1429 int lastUnknown = findLastUnknownMarkerSegmentPosition(); 1430 boolean hasJFIF = (findMarkerSegment(JFIFMarkerSegment.class, true) != null); 1431 int firstAdobe = findMarkerSegmentPosition(AdobeMarkerSegment.class, true); 1432 if (lastUnknown != -1) { 1433 markerSequence.add(lastUnknown+1, newGuy); 1434 } else if (hasJFIF) { 1435 markerSequence.add(1, newGuy); } if (firstAdobe != -1) { 1437 markerSequence.add(firstAdobe, newGuy); 1438 } else { 1439 markerSequence.add(0, newGuy); 1440 } 1441 } 1442 1443 1455 private void mergeSOFNode(Node node) throws IIOInvalidTreeException { 1456 SOFMarkerSegment sof = 1457 (SOFMarkerSegment) findMarkerSegment(SOFMarkerSegment.class, true); 1458 if (sof != null) { 1459 sof.updateFromNativeNode(node, false); 1460 } else { 1461 SOFMarkerSegment newGuy = new SOFMarkerSegment(node); 1462 int firstSOS = findMarkerSegmentPosition(SOSMarkerSegment.class, true); 1463 if (firstSOS != -1) { 1464 markerSequence.add(firstSOS, newGuy); 1465 } else { 1466 markerSequence.add(newGuy); 1467 } 1468 } 1469 } 1470 1471 1481 private void mergeSOSNode(Node node) throws IIOInvalidTreeException { 1482 SOSMarkerSegment firstSOS = 1483 (SOSMarkerSegment) findMarkerSegment(SOSMarkerSegment.class, true); 1484 SOSMarkerSegment lastSOS = 1485 (SOSMarkerSegment) findMarkerSegment(SOSMarkerSegment.class, false); 1486 if (firstSOS != null) { 1487 if (firstSOS != lastSOS) { 1488 throw new IIOInvalidTreeException 1489 ("Can't merge SOS node into a tree with > 1 SOS node", node); 1490 } 1491 firstSOS.updateFromNativeNode(node, false); 1492 } else { 1493 markerSequence.add(new SOSMarkerSegment(node)); 1494 } 1495 } 1496 1497 private boolean transparencyDone; 1498 1499 private void mergeStandardTree(Node root) throws IIOInvalidTreeException { 1500 transparencyDone = false; 1501 NodeList children = root.getChildNodes(); 1502 for (int i = 0; i < children.getLength(); i++) { 1503 Node node = children.item(i); 1504 String name = node.getNodeName(); 1505 if (name.equals("Chroma")) { 1506 mergeStandardChromaNode(node, children); 1507 } else if (name.equals("Compression")) { 1508 mergeStandardCompressionNode(node); 1509 } else if (name.equals("Data")) { 1510 mergeStandardDataNode(node); 1511 } else if (name.equals("Dimension")) { 1512 mergeStandardDimensionNode(node); 1513 } else if (name.equals("Document")) { 1514 mergeStandardDocumentNode(node); 1515 } else if (name.equals("Text")) { 1516 mergeStandardTextNode(node); 1517 } else if (name.equals("Transparency")) { 1518 mergeStandardTransparencyNode(node); 1519 } else { 1520 throw new IIOInvalidTreeException ("Invalid node: " + name, node); 1521 } 1522 } 1523 } 1524 1525 1533 1534 private void mergeStandardChromaNode(Node node, NodeList siblings) 1535 throws IIOInvalidTreeException { 1536 1544 if (transparencyDone) { 1545 throw new IIOInvalidTreeException 1546 ("Transparency node must follow Chroma node", node); 1547 } 1548 1549 Node csType = node.getFirstChild(); 1550 if ((csType == null) || !csType.getNodeName().equals("ColorSpaceType")) { 1551 return; 1553 } 1554 1555 String csName = csType.getAttributes().getNamedItem("name").getNodeValue(); 1556 1557 int numChannels = 0; 1558 boolean wantJFIF = false; 1559 boolean wantAdobe = false; 1560 int transform = 0; 1561 boolean willSubsample = false; 1562 byte [] ids = {1, 2, 3, 4}; if (csName.equals("GRAY")) { 1564 numChannels = 1; 1565 wantJFIF = true; 1566 } else if (csName.equals("YCbCr")) { 1567 numChannels = 3; 1568 wantJFIF = true; 1569 willSubsample = true; 1570 } else if (csName.equals("PhotoYCC")) { 1571 numChannels = 3; 1572 wantAdobe = true; 1573 transform = JPEG.ADOBE_YCC; 1574 ids[0] = (byte) 'Y'; 1575 ids[1] = (byte) 'C'; 1576 ids[2] = (byte) 'c'; 1577 } else if (csName.equals("RGB")) { 1578 numChannels = 3; 1579 wantAdobe = true; 1580 transform = JPEG.ADOBE_UNKNOWN; 1581 ids[0] = (byte) 'R'; 1582 ids[1] = (byte) 'G'; 1583 ids[2] = (byte) 'B'; 1584 } else if ((csName.equals("XYZ")) 1585 || (csName.equals("Lab")) 1586 || (csName.equals("Luv")) 1587 || (csName.equals("YxY")) 1588 || (csName.equals("HSV")) 1589 || (csName.equals("HLS")) 1590 || (csName.equals("CMY")) 1591 || (csName.equals("3CLR"))) { 1592 numChannels = 3; 1593 } else if (csName.equals("YCCK")) { 1594 numChannels = 4; 1595 wantAdobe = true; 1596 transform = JPEG.ADOBE_YCCK; 1597 willSubsample = true; 1598 } else if (csName.equals("CMYK")) { 1599 numChannels = 4; 1600 wantAdobe = true; 1601 transform = JPEG.ADOBE_UNKNOWN; 1602 } else if (csName.equals("4CLR")) { 1603 numChannels = 4; 1604 } else { return; 1606 } 1607 1608 boolean wantAlpha = false; 1609 for (int i = 0; i < siblings.getLength(); i++) { 1610 Node trans = siblings.item(i); 1611 if (trans.getNodeName().equals("Transparency")) { 1612 wantAlpha = wantAlpha(trans); 1613 break; } 1615 } 1616 1617 if (wantAlpha) { 1618 numChannels++; 1619 wantJFIF = false; 1620 if (ids[0] == (byte) 'R') { 1621 ids[3] = (byte) 'A'; 1622 wantAdobe = false; 1623 } 1624 } 1625 1626 JFIFMarkerSegment jfif = 1627 (JFIFMarkerSegment) findMarkerSegment(JFIFMarkerSegment.class, true); 1628 AdobeMarkerSegment adobe = 1629 (AdobeMarkerSegment) findMarkerSegment(AdobeMarkerSegment.class, true); 1630 SOFMarkerSegment sof = 1631 (SOFMarkerSegment) findMarkerSegment(SOFMarkerSegment.class, true); 1632 SOSMarkerSegment sos = 1633 (SOSMarkerSegment) findMarkerSegment(SOSMarkerSegment.class, true); 1634 1635 if ((sof != null) && (sof.tag == JPEG.SOF2)) { if ((sof.componentSpecs.length != numChannels) && (sos != null)) { 1643 return; 1644 } 1645 } 1646 1647 if (!wantJFIF && (jfif != null)) { 1649 markerSequence.remove(jfif); 1650 } 1651 1652 if (wantJFIF && !isStream) { 1654 markerSequence.add(0, new JFIFMarkerSegment()); 1655 } 1656 1657 if (wantAdobe) { 1660 if ((adobe == null) && !isStream) { 1661 adobe = new AdobeMarkerSegment(transform); 1662 insertAdobeMarkerSegment(adobe); 1663 } else { 1664 adobe.transform = transform; 1665 } 1666 } else if (adobe != null) { 1667 markerSequence.remove(adobe); 1668 } 1669 1670 boolean updateQtables = false; 1671 boolean updateHtables = false; 1672 1673 boolean progressive = false; 1674 1675 int [] subsampledSelectors = {0, 1, 1, 0 } ; 1676 int [] nonSubsampledSelectors = { 0, 0, 0, 0}; 1677 1678 int [] newTableSelectors = willSubsample 1679 ? subsampledSelectors 1680 : nonSubsampledSelectors; 1681 1682 SOFMarkerSegment.ComponentSpec [] oldCompSpecs = null; 1684 if (sof != null) { 1686 oldCompSpecs = sof.componentSpecs; 1687 progressive = (sof.tag == JPEG.SOF2); 1688 markerSequence.set(markerSequence.indexOf(sof), 1691 new SOFMarkerSegment(progressive, 1692 false, willSubsample, 1694 ids, 1695 numChannels)); 1696 1697 for (int i = 0; i < oldCompSpecs.length; i++) { 1703 if (oldCompSpecs[i].QtableSelector != newTableSelectors[i]) { 1704 updateQtables = true; 1705 } 1706 } 1707 1708 if (progressive) { 1709 boolean idsDiffer = false; 1712 for (int i = 0; i < oldCompSpecs.length; i++) { 1713 if (ids[i] != oldCompSpecs[i].componentId) { 1714 idsDiffer = true; 1715 } 1716 } 1717 if (idsDiffer) { 1718 for (Iterator iter = markerSequence.iterator(); iter.hasNext();) { 1720 MarkerSegment seg = (MarkerSegment) iter.next(); 1721 if (seg instanceof SOSMarkerSegment) { 1722 SOSMarkerSegment target = (SOSMarkerSegment) seg; 1723 for (int i = 0; i < target.componentSpecs.length; i++) { 1724 int oldSelector = 1725 target.componentSpecs[i].componentSelector; 1726 for (int j = 0; j < oldCompSpecs.length; j++) { 1733 if (oldCompSpecs[j].componentId == oldSelector) { 1734 target.componentSpecs[i].componentSelector = 1735 ids[j]; 1736 } 1737 } 1738 } 1739 } 1740 } 1741 } 1742 } else { 1743 if (sos != null) { 1744 for (int i = 0; i < sos.componentSpecs.length; i++) { 1747 if ((sos.componentSpecs[i].dcHuffTable 1748 != newTableSelectors[i]) 1749 || (sos.componentSpecs[i].acHuffTable 1750 != newTableSelectors[i])) { 1751 updateHtables = true; 1752 } 1753 } 1754 1755 markerSequence.set(markerSequence.indexOf(sos), 1757 new SOSMarkerSegment(willSubsample, 1758 ids, 1759 numChannels)); 1760 } 1761 } 1762 } else { 1763 if (isStream) { 1765 updateQtables = true; 1767 updateHtables = true; 1768 } 1769 } 1770 1771 if (updateQtables) { 1772 List tableSegments = new ArrayList (); 1773 for (Iterator iter = markerSequence.iterator(); iter.hasNext();) { 1774 MarkerSegment seg = (MarkerSegment) iter.next(); 1775 if (seg instanceof DQTMarkerSegment) { 1776 tableSegments.add(seg); 1777 } 1778 } 1779 if (!tableSegments.isEmpty() && willSubsample) { 1783 1788 boolean found = false; 1790 for (Iterator iter = tableSegments.iterator(); iter.hasNext();) { 1791 DQTMarkerSegment testdqt = (DQTMarkerSegment) iter.next(); 1792 for (Iterator tabiter = testdqt.tables.iterator(); 1793 tabiter.hasNext();) { 1794 DQTMarkerSegment.Qtable tab = 1795 (DQTMarkerSegment.Qtable) tabiter.next(); 1796 if (tab.tableID == 1) { 1797 found = true; 1798 } 1799 } 1800 } 1801 if (!found) { 1802 DQTMarkerSegment.Qtable table0 = null; 1804 for (Iterator iter = tableSegments.iterator(); iter.hasNext();) { 1805 DQTMarkerSegment testdqt = (DQTMarkerSegment) iter.next(); 1806 for (Iterator tabiter = testdqt.tables.iterator(); 1807 tabiter.hasNext();) { 1808 DQTMarkerSegment.Qtable tab = 1809 (DQTMarkerSegment.Qtable) tabiter.next(); 1810 if (tab.tableID == 0) { 1811 table0 = tab; 1812 } 1813 } 1814 } 1815 1816 DQTMarkerSegment dqt = 1820 (DQTMarkerSegment) tableSegments.get(tableSegments.size()-1); 1821 dqt.tables.add(dqt.getChromaForLuma(table0)); 1822 } 1823 } 1824 } 1825 1826 if (updateHtables) { 1827 List tableSegments = new ArrayList (); 1828 for (Iterator iter = markerSequence.iterator(); iter.hasNext();) { 1829 MarkerSegment seg = (MarkerSegment) iter.next(); 1830 if (seg instanceof DHTMarkerSegment) { 1831 tableSegments.add(seg); 1832 } 1833 } 1834 if (!tableSegments.isEmpty() && willSubsample) { 1838 1842 boolean found = false; 1844 for (Iterator iter = tableSegments.iterator(); iter.hasNext();) { 1845 DHTMarkerSegment testdht = (DHTMarkerSegment) iter.next(); 1846 for (Iterator tabiter = testdht.tables.iterator(); 1847 tabiter.hasNext();) { 1848 DHTMarkerSegment.Htable tab = 1849 (DHTMarkerSegment.Htable) tabiter.next(); 1850 if (tab.tableID == 1) { 1851 found = true; 1852 } 1853 } 1854 } 1855 if (!found) { 1856 DHTMarkerSegment lastDHT = 1859 (DHTMarkerSegment) tableSegments.get(tableSegments.size()-1); 1860 lastDHT.addHtable(JPEGHuffmanTable.StdDCLuminance, true, 1); 1861 lastDHT.addHtable(JPEGHuffmanTable.StdACLuminance, true, 1); 1862 } 1863 } 1864 } 1865 } 1866 1867 private boolean wantAlpha(Node transparency) { 1868 boolean returnValue = false; 1869 Node alpha = transparency.getFirstChild(); if (alpha.getNodeName().equals("Alpha")) { 1871 if (alpha.hasAttributes()) { 1872 String value = 1873 alpha.getAttributes().getNamedItem("value").getNodeValue(); 1874 if (!value.equals("none")) { 1875 returnValue = true; 1876 } 1877 } 1878 } 1879 transparencyDone = true; 1880 return returnValue; 1881 } 1882 1883 private void mergeStandardCompressionNode(Node node) 1884 throws IIOInvalidTreeException { 1885 } 1889 1890 private void mergeStandardDataNode(Node node) 1891 throws IIOInvalidTreeException { 1892 } 1894 1895 private void mergeStandardDimensionNode(Node node) 1896 throws IIOInvalidTreeException { 1897 JFIFMarkerSegment jfif = 1900 (JFIFMarkerSegment) findMarkerSegment(JFIFMarkerSegment.class, true); 1901 if (jfif == null) { 1902 boolean canHaveJFIF = false; 1907 SOFMarkerSegment sof = 1908 (SOFMarkerSegment) findMarkerSegment(SOFMarkerSegment.class, true); 1909 if (sof != null) { 1910 int numChannels = sof.componentSpecs.length; 1911 if ((numChannels == 1) || (numChannels == 3)) { 1912 canHaveJFIF = true; for (int i = 0; i < sof.componentSpecs.length; i++) { 1914 if (sof.componentSpecs[i].componentId != i+1) 1915 canHaveJFIF = false; 1916 } 1917 AdobeMarkerSegment adobe = 1920 (AdobeMarkerSegment) findMarkerSegment(AdobeMarkerSegment.class, 1921 true); 1922 if (adobe != null) { 1923 if (adobe.transform != ((numChannels == 1) 1924 ? JPEG.ADOBE_UNKNOWN 1925 : JPEG.ADOBE_YCC)) { 1926 canHaveJFIF = false; 1927 } 1928 } 1929 } 1930 } 1931 if (canHaveJFIF) { 1934 jfif = new JFIFMarkerSegment(); 1935 markerSequence.add(0, jfif); 1936 } 1937 } 1938 if (jfif != null) { 1939 NodeList children = node.getChildNodes(); 1940 for (int i = 0; i < children.getLength(); i++) { 1941 Node child = children.item(i); 1942 NamedNodeMap attrs = child.getAttributes(); 1943 String name = child.getNodeName(); 1944 if (name.equals("PixelAspectRatio")) { 1945 String valueString = attrs.getNamedItem("value").getNodeValue(); 1946 float value = Float.parseFloat(valueString); 1947 Point p = findIntegerRatio(value); 1948 jfif.resUnits = JPEG.DENSITY_UNIT_ASPECT_RATIO; 1949 jfif.Xdensity = p.x; 1950 jfif.Xdensity = p.y; 1951 } else if (name.equals("HorizontalPixelSize")) { 1952 String valueString = attrs.getNamedItem("value").getNodeValue(); 1953 float value = Float.parseFloat(valueString); 1954 int dpcm = (int) Math.round(1.0/(value*10.0)); 1956 jfif.resUnits = JPEG.DENSITY_UNIT_DOTS_CM; 1957 jfif.Xdensity = dpcm; 1958 } else if (name.equals("VerticalPixelSize")) { 1959 String valueString = attrs.getNamedItem("value").getNodeValue(); 1960 float value = Float.parseFloat(valueString); 1961 int dpcm = (int) Math.round(1.0/(value*10.0)); 1963 jfif.resUnits = JPEG.DENSITY_UNIT_DOTS_CM; 1964 jfif.Ydensity = dpcm; 1965 } 1966 1967 } 1968 } 1969 } 1970 1971 1975 private static Point findIntegerRatio(float value) { 1976 float epsilon = 0.005F; 1977 1978 value = Math.abs(value); 1980 1981 if (value <= epsilon) { 1983 return new Point (1, 255); 1984 } 1985 1986 if (value >= 255) { 1988 return new Point (255, 1); 1989 } 1990 1991 boolean inverted = false; 1993 if (value < 1.0) { 1994 value = 1.0F/value; 1995 inverted = true; 1996 } 1997 1998 int y = 1; 2000 int x = (int) Math.round(value); 2001 2002 float ratio = (float) x; 2003 float delta = Math.abs(value - ratio); 2004 while (delta > epsilon) { y++; 2007 x = (int) Math.round(y*value); 2008 ratio = (float)x/(float)y; 2009 delta = Math.abs(value - ratio); 2010 } 2011 return inverted ? new Point (y, x) : new Point (x, y); 2012 } 2013 2014 private void mergeStandardDocumentNode(Node node) 2015 throws IIOInvalidTreeException { 2016 } 2018 2019 private void mergeStandardTextNode(Node node) 2020 throws IIOInvalidTreeException { 2021 NodeList children = node.getChildNodes(); 2025 for (int i = 0; i < children.getLength(); i++) { 2026 Node child = children.item(i); 2027 NamedNodeMap attrs = child.getAttributes(); 2028 Node comp = attrs.getNamedItem("compression"); 2029 boolean copyIt = true; 2030 if (comp != null) { 2031 String compString = comp.getNodeValue(); 2032 if (!compString.equals("none")) { 2033 copyIt = false; 2034 } 2035 } 2036 if (copyIt) { 2037 String value = attrs.getNamedItem("value").getNodeValue(); 2038 COMMarkerSegment com = new COMMarkerSegment(value); 2039 insertCOMMarkerSegment(com); 2040 } 2041 } 2042 } 2043 2044 private void mergeStandardTransparencyNode(Node node) 2045 throws IIOInvalidTreeException { 2046 if (!transparencyDone && !isStream) { 2051 boolean wantAlpha = wantAlpha(node); 2052 JFIFMarkerSegment jfif = (JFIFMarkerSegment) findMarkerSegment 2056 (JFIFMarkerSegment.class, true); 2057 AdobeMarkerSegment adobe = (AdobeMarkerSegment) findMarkerSegment 2058 (AdobeMarkerSegment.class, true); 2059 SOFMarkerSegment sof = (SOFMarkerSegment) findMarkerSegment 2060 (SOFMarkerSegment.class, true); 2061 SOSMarkerSegment sos = (SOSMarkerSegment) findMarkerSegment 2062 (SOSMarkerSegment.class, true); 2063 2064 if ((sof != null) && (sof.tag == JPEG.SOF2)) { return; 2068 } 2069 2070 if (sof != null) { 2073 int numChannels = sof.componentSpecs.length; 2074 boolean hadAlpha = (numChannels == 2) || (numChannels == 4); 2075 if (hadAlpha != wantAlpha) { 2077 if (wantAlpha) { numChannels++; 2079 if (jfif != null) { 2080 markerSequence.remove(jfif); 2081 } 2082 2083 if (adobe != null) { 2085 adobe.transform = JPEG.ADOBE_UNKNOWN; 2086 } 2087 2088 SOFMarkerSegment.ComponentSpec [] newSpecs = 2090 new SOFMarkerSegment.ComponentSpec[numChannels]; 2091 for (int i = 0; i < sof.componentSpecs.length; i++) { 2092 newSpecs[i] = sof.componentSpecs[i]; 2093 } 2094 byte oldFirstID = (byte) sof.componentSpecs[0].componentId; 2095 byte newID = (byte) ((oldFirstID > 1) ? 'A' : 4); 2096 newSpecs[numChannels-1] = 2097 sof.getComponentSpec(newID, 2098 sof.componentSpecs[0].HsamplingFactor, 2099 sof.componentSpecs[0].QtableSelector); 2100 2101 sof.componentSpecs = newSpecs; 2102 2103 SOSMarkerSegment.ScanComponentSpec [] newScanSpecs = 2105 new SOSMarkerSegment.ScanComponentSpec [numChannels]; 2106 for (int i = 0; i < sos.componentSpecs.length; i++) { 2107 newScanSpecs[i] = sos.componentSpecs[i]; 2108 } 2109 newScanSpecs[numChannels-1] = 2110 sos.getScanComponentSpec (newID, 0); 2111 sos.componentSpecs = newScanSpecs; 2112 } else { numChannels--; 2114 SOFMarkerSegment.ComponentSpec [] newSpecs = 2116 new SOFMarkerSegment.ComponentSpec[numChannels]; 2117 for (int i = 0; i < numChannels; i++) { 2118 newSpecs[i] = sof.componentSpecs[i]; 2119 } 2120 sof.componentSpecs = newSpecs; 2121 2122 SOSMarkerSegment.ScanComponentSpec [] newScanSpecs = 2124 new SOSMarkerSegment.ScanComponentSpec [numChannels]; 2125 for (int i = 0; i < numChannels; i++) { 2126 newScanSpecs[i] = sos.componentSpecs[i]; 2127 } 2128 sos.componentSpecs = newScanSpecs; 2129 } 2130 } 2131 } 2132 } 2133 } 2134 2135 2136 public void setFromTree(String formatName, Node root) 2137 throws IIOInvalidTreeException { 2138 if (formatName == null) { 2139 throw new IllegalArgumentException ("null formatName!"); 2140 } 2141 if (root == null) { 2142 throw new IllegalArgumentException ("null root!"); 2143 } 2144 if (isStream && 2145 (formatName.equals(JPEG.nativeStreamMetadataFormatName))) { 2146 setFromNativeTree(root); 2147 } else if (!isStream && 2148 (formatName.equals(JPEG.nativeImageMetadataFormatName))) { 2149 setFromNativeTree(root); 2150 } else if (!isStream && 2151 (formatName.equals 2152 (IIOMetadataFormatImpl.standardMetadataFormatName))) { 2153 super.setFromTree(formatName, root); 2155 } else { 2156 throw new IllegalArgumentException ("Unsupported format name: " 2157 + formatName); 2158 } 2159 } 2160 2161 private void setFromNativeTree(Node root) throws IIOInvalidTreeException { 2162 if (resetSequence == null) { 2163 resetSequence = markerSequence; 2164 } 2165 markerSequence = new ArrayList (); 2166 2167 2169 String name = root.getNodeName(); 2170 if (name != ((isStream) ? JPEG.nativeStreamMetadataFormatName 2171 : JPEG.nativeImageMetadataFormatName)) { 2172 throw new IIOInvalidTreeException ("Invalid root node name: " + name, 2173 root); 2174 } 2175 if (!isStream) { 2176 if (root.getChildNodes().getLength() != 2) { throw new IIOInvalidTreeException ( 2178 "JPEGvariety and markerSequence nodes must be present", root); 2179 } 2180 2181 Node JPEGvariety = root.getFirstChild(); 2182 2183 if (JPEGvariety.getChildNodes().getLength() != 0) { 2184 markerSequence.add(new JFIFMarkerSegment(JPEGvariety.getFirstChild())); 2185 } 2186 } 2187 2188 Node markerSequenceNode = isStream ? root : root.getLastChild(); 2189 setFromMarkerSequenceNode(markerSequenceNode); 2190 2191 } 2192 2193 void setFromMarkerSequenceNode(Node markerSequenceNode) 2194 throws IIOInvalidTreeException { 2195 2196 NodeList children = markerSequenceNode.getChildNodes(); 2197 for (int i = 0; i < children.getLength(); i++) { 2199 Node node = children.item(i); 2200 String childName = node.getNodeName(); 2201 if (childName.equals("dqt")) { 2202 markerSequence.add(new DQTMarkerSegment(node)); 2203 } else if (childName.equals("dht")) { 2204 markerSequence.add(new DHTMarkerSegment(node)); 2205 } else if (childName.equals("dri")) { 2206 markerSequence.add(new DRIMarkerSegment(node)); 2207 } else if (childName.equals("com")) { 2208 markerSequence.add(new COMMarkerSegment(node)); 2209 } else if (childName.equals("app14Adobe")) { 2210 markerSequence.add(new AdobeMarkerSegment(node)); 2211 } else if (childName.equals("unknown")) { 2212 markerSequence.add(new MarkerSegment(node)); 2213 } else if (childName.equals("sof")) { 2214 markerSequence.add(new SOFMarkerSegment(node)); 2215 } else if (childName.equals("sos")) { 2216 markerSequence.add(new SOSMarkerSegment(node)); 2217 } else { 2218 throw new IIOInvalidTreeException ("Invalid " 2219 + (isStream ? "stream " : "image ") + "child: " 2220 + childName, node); 2221 } 2222 } 2223 } 2224 2225 2232 private boolean isConsistent() { 2233 SOFMarkerSegment sof = 2234 (SOFMarkerSegment) findMarkerSegment(SOFMarkerSegment.class, 2235 true); 2236 JFIFMarkerSegment jfif = 2237 (JFIFMarkerSegment) findMarkerSegment(JFIFMarkerSegment.class, 2238 true); 2239 AdobeMarkerSegment adobe = 2240 (AdobeMarkerSegment) findMarkerSegment(AdobeMarkerSegment.class, 2241 true); 2242 boolean retval = true; 2243 if (!isStream) { 2244 if (sof != null) { 2245 int numSOFBands = sof.componentSpecs.length; 2247 int numScanBands = countScanBands(); 2248 if (numScanBands != 0) { if (numScanBands != numSOFBands) { 2250 retval = false; 2251 } 2252 } 2253 if (jfif != null) { 2255 if ((numSOFBands != 1) && (numSOFBands != 3)) { 2256 retval = false; 2257 } 2258 for (int i = 0; i < numSOFBands; i++) { 2259 if (sof.componentSpecs[i].componentId != i+1) { 2260 retval = false; 2261 } 2262 } 2263 2264 if ((adobe != null) 2268 && (((numSOFBands == 1) 2269 && (adobe.transform != JPEG.ADOBE_UNKNOWN)) 2270 || ((numSOFBands == 3) 2271 && (adobe.transform != JPEG.ADOBE_YCC)))) { 2272 retval = false; 2273 } 2274 } 2275 } else { 2276 SOSMarkerSegment sos = 2278 (SOSMarkerSegment) findMarkerSegment(SOSMarkerSegment.class, 2279 true); 2280 if ((jfif != null) || (adobe != null) 2281 || (sof != null) || (sos != null)) { 2282 retval = false; 2283 } 2284 } 2285 } 2286 return retval; 2287 } 2288 2289 2293 private int countScanBands() { 2294 List ids = new ArrayList (); 2295 Iterator iter = markerSequence.iterator(); 2296 while(iter.hasNext()) { 2297 MarkerSegment seg = (MarkerSegment)iter.next(); 2298 if (seg instanceof SOSMarkerSegment) { 2299 SOSMarkerSegment sos = (SOSMarkerSegment) seg; 2300 SOSMarkerSegment.ScanComponentSpec [] specs = sos.componentSpecs; 2301 for (int i = 0; i < specs.length; i++) { 2302 Integer id = new Integer (specs[i].componentSelector); 2303 if (!ids.contains(id)) { 2304 ids.add(id); 2305 } 2306 } 2307 } 2308 } 2309 2310 return ids.size(); 2311 } 2312 2313 2315 void writeToStream(ImageOutputStream ios, 2316 boolean ignoreJFIF, 2317 boolean forceJFIF, 2318 List thumbnails, 2319 ICC_Profile iccProfile, 2320 boolean ignoreAdobe, 2321 int newAdobeTransform, 2322 JPEGImageWriter writer) 2323 throws IOException { 2324 if (forceJFIF) { 2325 JFIFMarkerSegment.writeDefaultJFIF(ios, 2329 thumbnails, 2330 iccProfile, 2331 writer); 2332 if ((ignoreAdobe == false) 2333 && (newAdobeTransform != JPEG.ADOBE_IMPOSSIBLE)) { 2334 if ((newAdobeTransform != JPEG.ADOBE_UNKNOWN) 2335 && (newAdobeTransform != JPEG.ADOBE_YCC)) { 2336 ignoreAdobe = true; 2338 writer.warningOccurred 2339 (JPEGImageWriter.WARNING_METADATA_ADJUSTED_FOR_THUMB); 2340 } 2341 } 2342 } 2343 Iterator iter = markerSequence.iterator(); 2345 while(iter.hasNext()) { 2346 MarkerSegment seg = (MarkerSegment)iter.next(); 2347 if (seg instanceof JFIFMarkerSegment) { 2348 if (ignoreJFIF == false) { 2349 JFIFMarkerSegment jfif = (JFIFMarkerSegment) seg; 2350 jfif.writeWithThumbs(ios, thumbnails, writer); 2351 if (iccProfile != null) { 2352 JFIFMarkerSegment.writeICC(iccProfile, ios); 2353 } 2354 } } else if (seg instanceof AdobeMarkerSegment) { 2356 if (ignoreAdobe == false) { 2357 if (newAdobeTransform != JPEG.ADOBE_IMPOSSIBLE) { 2358 AdobeMarkerSegment newAdobe = 2359 (AdobeMarkerSegment) seg.clone(); 2360 newAdobe.transform = newAdobeTransform; 2361 newAdobe.write(ios); 2362 } else if (forceJFIF) { 2363 AdobeMarkerSegment adobe = (AdobeMarkerSegment) seg; 2365 if ((adobe.transform == JPEG.ADOBE_UNKNOWN) 2366 || (adobe.transform == JPEG.ADOBE_YCC)) { 2367 adobe.write(ios); 2368 } else { 2369 writer.warningOccurred 2370 (JPEGImageWriter.WARNING_METADATA_ADJUSTED_FOR_THUMB); 2371 } 2372 } else { 2373 seg.write(ios); 2374 } 2375 } } else { 2377 seg.write(ios); 2378 } 2379 } 2380 } 2381 2382 2384 public void reset() { 2385 if (resetSequence != null) { markerSequence = resetSequence; 2387 resetSequence = null; 2388 } 2389 } 2390 2391 public void print() { 2392 for (int i = 0; i < markerSequence.size(); i++) { 2393 MarkerSegment seg = (MarkerSegment) markerSequence.get(i); 2394 seg.print(); 2395 } 2396 } 2397 2398} 2399 | Popular Tags |