1 7 8 package com.sun.imageio.plugins.png; 9 10 import java.awt.Point ; 11 import java.awt.Rectangle ; 12 import java.awt.color.ColorSpace ; 13 import java.awt.image.BufferedImage ; 14 import java.awt.image.DataBuffer ; 15 import java.awt.image.DataBufferByte ; 16 import java.awt.image.DataBufferUShort ; 17 import java.awt.image.Raster ; 18 import java.awt.image.WritableRaster ; 19 import java.io.BufferedInputStream ; 20 import java.io.ByteArrayInputStream ; 21 import java.io.DataInputStream ; 22 import java.io.InputStream ; 23 import java.io.IOException ; 24 import java.io.SequenceInputStream ; 25 import java.util.ArrayList ; 26 import java.util.Arrays ; 27 import java.util.Enumeration ; 28 import java.util.Iterator ; 29 import java.util.List ; 30 import java.util.zip.Inflater ; 31 import java.util.zip.InflaterInputStream ; 32 import javax.imageio.IIOException ; 33 import javax.imageio.ImageReader ; 34 import javax.imageio.ImageReadParam ; 35 import javax.imageio.ImageTypeSpecifier ; 36 import javax.imageio.metadata.IIOMetadata ; 37 import javax.imageio.spi.ImageReaderSpi ; 38 import javax.imageio.stream.ImageInputStream ; 39 import com.sun.imageio.plugins.common.InputStreamAdapter; 40 import com.sun.imageio.plugins.common.SubImageInputStream; 41 42 class PNGImageDataEnumeration implements Enumeration { 43 44 boolean firstTime = true; 45 ImageInputStream stream; 46 int length; 47 48 public PNGImageDataEnumeration(ImageInputStream stream) 49 throws IOException { 50 this.stream = stream; 51 this.length = stream.readInt(); 52 int type = stream.readInt(); } 54 55 public Object nextElement() { 56 try { 57 firstTime = false; 58 ImageInputStream iis = new SubImageInputStream(stream, length); 59 return new InputStreamAdapter(iis); 60 } catch (IOException e) { 61 return null; 62 } 63 } 64 65 public boolean hasMoreElements() { 66 if (firstTime) { 67 return true; 68 } 69 70 try { 71 int crc = stream.readInt(); 72 this.length = stream.readInt(); 73 int type = stream.readInt(); 74 if (type == PNGImageReader.IDAT_TYPE) { 75 return true; 76 } else { 77 return false; 78 } 79 } catch (IOException e) { 80 return false; 81 } 82 } 83 } 84 85 88 public class PNGImageReader extends ImageReader { 89 90 static final int IHDR_TYPE = chunkType("IHDR"); 92 static final int PLTE_TYPE = chunkType("PLTE"); 93 static final int IDAT_TYPE = chunkType("IDAT"); 94 static final int IEND_TYPE = chunkType("IEND"); 95 96 static final int bKGD_TYPE = chunkType("bKGD"); 98 static final int cHRM_TYPE = chunkType("cHRM"); 99 static final int gAMA_TYPE = chunkType("gAMA"); 100 static final int hIST_TYPE = chunkType("hIST"); 101 static final int iCCP_TYPE = chunkType("iCCP"); 102 static final int iTXt_TYPE = chunkType("iTXt"); 103 static final int pHYs_TYPE = chunkType("pHYs"); 104 static final int sBIT_TYPE = chunkType("sBIT"); 105 static final int sPLT_TYPE = chunkType("sPLT"); 106 static final int sRGB_TYPE = chunkType("sRGB"); 107 static final int tEXt_TYPE = chunkType("tEXt"); 108 static final int tIME_TYPE = chunkType("tIME"); 109 static final int tRNS_TYPE = chunkType("tRNS"); 110 static final int zTXt_TYPE = chunkType("zTXt"); 111 112 static final int PNG_COLOR_GRAY = 0; 113 static final int PNG_COLOR_RGB = 2; 114 static final int PNG_COLOR_PALETTE = 3; 115 static final int PNG_COLOR_GRAY_ALPHA = 4; 116 static final int PNG_COLOR_RGB_ALPHA = 6; 117 118 static final int[] inputBandsForColorType = { 120 1, -1, 3, 1, 2, -1, 4 }; 128 129 static final int PNG_FILTER_NONE = 0; 130 static final int PNG_FILTER_SUB = 1; 131 static final int PNG_FILTER_UP = 2; 132 static final int PNG_FILTER_AVERAGE = 3; 133 static final int PNG_FILTER_PAETH = 4; 134 135 static final int[] adam7XOffset = { 0, 4, 0, 2, 0, 1, 0 }; 136 static final int[] adam7YOffset = { 0, 0, 4, 0, 2, 0, 1 }; 137 static final int[] adam7XSubsampling = { 8, 8, 4, 4, 2, 2, 1, 1 }; 138 static final int[] adam7YSubsampling = { 8, 8, 8, 4, 4, 2, 2, 1 }; 139 140 private static final boolean debug = true; 141 142 ImageInputStream stream = null; 143 144 boolean gotHeader = false; 145 boolean gotMetadata = false; 146 147 ImageReadParam lastParam = null; 148 149 long imageStartPosition = -1L; 150 151 Rectangle sourceRegion = null; 152 int sourceXSubsampling = -1; 153 int sourceYSubsampling = -1; 154 int sourceMinProgressivePass = 0; 155 int sourceMaxProgressivePass = 6; 156 int[] sourceBands = null; 157 int[] destinationBands = null; 158 Point destinationOffset = new Point (0, 0); 159 160 PNGMetadata metadata = new PNGMetadata(); 161 162 DataInputStream pixelStream = null; 163 164 BufferedImage theImage = null; 165 166 int pixelsDone = 0; 168 169 int totalPixels; 171 172 public PNGImageReader(ImageReaderSpi originatingProvider) { 173 super(originatingProvider); 174 } 175 176 public void setInput(Object input, 177 boolean seekForwardOnly, 178 boolean ignoreMetadata) { 179 super.setInput(input, seekForwardOnly, ignoreMetadata); 180 this.stream = (ImageInputStream )input; 182 resetStreamSettings(); 184 } 185 186 static int chunkType(String typeString) { 188 char c0 = typeString.charAt(0); 189 char c1 = typeString.charAt(1); 190 char c2 = typeString.charAt(2); 191 char c3 = typeString.charAt(3); 192 193 int type = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3; 194 return type; 195 } 196 197 private String readNullTerminatedString() throws IOException { 198 StringBuffer b = new StringBuffer (); 199 int c; 200 201 while ((c = stream.read()) != 0) { 202 b.append((char)c); 203 } 204 return b.toString(); 205 } 206 207 private void readHeader() throws IIOException { 208 if (gotHeader) { 209 return; 210 } 211 if (stream == null) { 212 throw new IllegalStateException ("Input source not set!"); 213 } 214 215 try { 216 byte[] signature = new byte[8]; 217 stream.readFully(signature); 218 219 if (signature[0] != (byte)137 || 220 signature[1] != (byte)80 || 221 signature[2] != (byte)78 || 222 signature[3] != (byte)71 || 223 signature[4] != (byte)13 || 224 signature[5] != (byte)10 || 225 signature[6] != (byte)26 || 226 signature[7] != (byte)10) { 227 throw new IIOException ("Bad PNG signature!"); 228 } 229 230 int IHDR_length = stream.readInt(); 231 if (IHDR_length != 13) { 232 throw new IIOException ("Bad length for IHDR chunk!"); 233 } 234 int IHDR_type = stream.readInt(); 235 if (IHDR_type != IHDR_TYPE) { 236 throw new IIOException ("Bad type for IHDR chunk!"); 237 } 238 239 this.metadata = new PNGMetadata(); 240 241 int width = stream.readInt(); 242 int height = stream.readInt(); 243 int bitDepth = stream.readUnsignedByte(); 244 int colorType = stream.readUnsignedByte(); 245 int compressionMethod = stream.readUnsignedByte(); 246 int filterMethod = stream.readUnsignedByte(); 247 int interlaceMethod = stream.readUnsignedByte(); 248 249 int IHDR_CRC = stream.readInt(); 250 251 stream.flushBefore(stream.getStreamPosition()); 252 253 if (width == 0) { 254 throw new IIOException ("Image width == 0!"); 255 } 256 if (height == 0) { 257 throw new IIOException ("Image height == 0!"); 258 } 259 if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && 260 bitDepth != 8 && bitDepth != 16) { 261 throw new IIOException ("Bit depth must be 1, 2, 4, 8, or 16!"); 262 } 263 if (colorType != 0 && colorType != 2 && colorType != 3 && 264 colorType != 4 && colorType != 6) { 265 throw new IIOException ("Color type must be 0, 2, 3, 4, or 6!"); 266 } 267 if (colorType == PNG_COLOR_PALETTE && bitDepth == 16) { 268 throw new IIOException ("Bad color type/bit depth combination!"); 269 } 270 if ((colorType == PNG_COLOR_RGB || 271 colorType == PNG_COLOR_RGB_ALPHA || 272 colorType == PNG_COLOR_GRAY_ALPHA) && 273 (bitDepth != 8 && bitDepth != 16)) { 274 throw new IIOException ("Bad color type/bit depth combination!"); 275 } 276 if (compressionMethod != 0) { 277 throw new IIOException ("Unknown compression method (not 0)!"); 278 } 279 if (filterMethod != 0) { 280 throw new IIOException ("Unknown filter method (not 0)!"); 281 } 282 if (interlaceMethod != 0 && interlaceMethod != 1) { 283 throw new IIOException ("Unknown interlace method (not 0 or 1)!"); 284 } 285 286 metadata.IHDR_present = true; 287 metadata.IHDR_width = width; 288 metadata.IHDR_height = height; 289 metadata.IHDR_bitDepth = bitDepth; 290 metadata.IHDR_colorType = colorType; 291 metadata.IHDR_compressionMethod = compressionMethod; 292 metadata.IHDR_filterMethod = filterMethod; 293 metadata.IHDR_interlaceMethod = interlaceMethod; 294 gotHeader = true; 295 } catch (IOException e) { 296 throw new IIOException ("I/O error reading PNG header!", e); 297 } 298 } 299 300 private void parse_PLTE_chunk(int chunkLength) throws IOException { 301 if (metadata.PLTE_present) { 302 processWarningOccurred( 303 "A PNG image may not contain more than one PLTE chunk.\n" + 304 "The chunk wil be ignored."); 305 return; 306 } else if (metadata.IHDR_colorType == PNG_COLOR_GRAY || 307 metadata.IHDR_colorType == PNG_COLOR_GRAY_ALPHA) { 308 processWarningOccurred( 309 "A PNG gray or gray alpha image cannot have a PLTE chunk.\n" + 310 "The chunk wil be ignored."); 311 return; 312 } 313 314 byte[] palette = new byte[chunkLength]; 315 stream.readFully(palette); 316 317 int numEntries = chunkLength/3; 318 if (metadata.IHDR_colorType == PNG_COLOR_PALETTE) { 319 int maxEntries = 1 << metadata.IHDR_bitDepth; 320 if (numEntries > maxEntries) { 321 processWarningOccurred( 322 "PLTE chunk contains too many entries for bit depth, ignoring extras."); 323 numEntries = maxEntries; 324 } 325 numEntries = Math.min(numEntries, maxEntries); 326 } 327 328 int paletteEntries; 330 if (numEntries > 16) { 331 paletteEntries = 256; 332 } else if (numEntries > 4) { 333 paletteEntries = 16; 334 } else if (numEntries > 2) { 335 paletteEntries = 4; 336 } else { 337 paletteEntries = 2; 338 } 339 340 metadata.PLTE_present = true; 341 metadata.PLTE_red = new byte[paletteEntries]; 342 metadata.PLTE_green = new byte[paletteEntries]; 343 metadata.PLTE_blue = new byte[paletteEntries]; 344 345 int index = 0; 346 for (int i = 0; i < numEntries; i++) { 347 metadata.PLTE_red[i] = palette[index++]; 348 metadata.PLTE_green[i] = palette[index++]; 349 metadata.PLTE_blue[i] = palette[index++]; 350 } 351 } 352 353 private void parse_bKGD_chunk() throws IOException { 354 if (metadata.IHDR_colorType == PNG_COLOR_PALETTE) { 355 metadata.bKGD_colorType = PNG_COLOR_PALETTE; 356 metadata.bKGD_index = stream.readUnsignedByte(); 357 } else if (metadata.IHDR_colorType == PNG_COLOR_GRAY || 358 metadata.IHDR_colorType == PNG_COLOR_GRAY_ALPHA) { 359 metadata.bKGD_colorType = PNG_COLOR_GRAY; 360 metadata.bKGD_gray = stream.readUnsignedShort(); 361 } else { metadata.bKGD_colorType = PNG_COLOR_RGB; 363 metadata.bKGD_red = stream.readUnsignedShort(); 364 metadata.bKGD_green = stream.readUnsignedShort(); 365 metadata.bKGD_blue = stream.readUnsignedShort(); 366 } 367 368 metadata.bKGD_present = true; 369 } 370 371 private void parse_cHRM_chunk() throws IOException { 372 metadata.cHRM_whitePointX = stream.readInt(); 373 metadata.cHRM_whitePointY = stream.readInt(); 374 metadata.cHRM_redX = stream.readInt(); 375 metadata.cHRM_redY = stream.readInt(); 376 metadata.cHRM_greenX = stream.readInt(); 377 metadata.cHRM_greenY = stream.readInt(); 378 metadata.cHRM_blueX = stream.readInt(); 379 metadata.cHRM_blueY = stream.readInt(); 380 381 metadata.cHRM_present = true; 382 } 383 384 private void parse_gAMA_chunk() throws IOException { 385 int gamma = stream.readInt(); 386 metadata.gAMA_gamma = gamma; 387 388 metadata.gAMA_present = true; 389 } 390 391 private void parse_hIST_chunk() throws IOException , IIOException { 392 if (!metadata.PLTE_present) { 393 throw new IIOException ("hIST chunk without prior PLTE chunk!"); 394 } 395 396 metadata.hIST_histogram = new char[metadata.PLTE_red.length]; 397 stream.readFully(metadata.hIST_histogram, 398 0, metadata.hIST_histogram.length); 399 400 metadata.hIST_present = true; 401 } 402 403 private void parse_iCCP_chunk(int chunkLength) throws IOException { 404 String keyword = readNullTerminatedString(); 405 metadata.iCCP_profileName = keyword; 406 407 metadata.iCCP_compressionMethod = stream.readUnsignedByte(); 408 409 byte[] compressedProfile = 410 new byte[chunkLength - keyword.length() - 2]; 411 stream.readFully(compressedProfile); 412 metadata.iCCP_compressedProfile = compressedProfile; 413 414 metadata.iCCP_present = true; 415 } 416 417 private void parse_iTXt_chunk(int chunkLength) throws IOException { 418 long chunkStart = stream.getStreamPosition(); 419 420 String keyword = readNullTerminatedString(); 421 metadata.iTXt_keyword.add(keyword); 422 423 int compressionFlag = stream.readUnsignedByte(); 424 metadata.iTXt_compressionFlag.add(new Integer (compressionFlag)); 425 426 int compressionMethod = stream.readUnsignedByte(); 427 metadata.iTXt_compressionMethod.add(new Integer (compressionMethod)); 428 429 String languageTag = readNullTerminatedString(); 430 metadata.iTXt_languageTag.add(languageTag); 431 432 String translatedKeyword = stream.readUTF(); 433 metadata.iTXt_translatedKeyword.add(translatedKeyword); 434 stream.skipBytes(1); 436 String text; 437 if (compressionFlag == 1) { long pos = stream.getStreamPosition(); 439 byte[] b = new byte[(int)(chunkStart + chunkLength - pos)]; 440 stream.readFully(b); 441 text = inflate(b); 442 } else { 443 text = stream.readUTF(); 444 } 445 metadata.iTXt_text.add(text); 446 } 447 448 private void parse_pHYs_chunk() throws IOException { 449 metadata.pHYs_pixelsPerUnitXAxis = stream.readInt(); 450 metadata.pHYs_pixelsPerUnitYAxis = stream.readInt(); 451 metadata.pHYs_unitSpecifier = stream.readUnsignedByte(); 452 453 metadata.pHYs_present = true; 454 } 455 456 private void parse_sBIT_chunk() throws IOException { 457 int colorType = metadata.IHDR_colorType; 458 if (colorType == PNG_COLOR_GRAY || 459 colorType == PNG_COLOR_GRAY_ALPHA) { 460 metadata.sBIT_grayBits = stream.readUnsignedByte(); 461 } else if (colorType == PNG_COLOR_RGB || 462 colorType == PNG_COLOR_PALETTE || 463 colorType == PNG_COLOR_RGB_ALPHA) { 464 metadata.sBIT_redBits = stream.readUnsignedByte(); 465 metadata.sBIT_greenBits = stream.readUnsignedByte(); 466 metadata.sBIT_blueBits = stream.readUnsignedByte(); 467 } 468 469 if (colorType == PNG_COLOR_GRAY_ALPHA || 470 colorType == PNG_COLOR_RGB_ALPHA) { 471 metadata.sBIT_alphaBits = stream.readUnsignedByte(); 472 } 473 474 metadata.sBIT_colorType = colorType; 475 metadata.sBIT_present = true; 476 } 477 478 private void parse_sPLT_chunk(int chunkLength) 479 throws IOException , IIOException { 480 metadata.sPLT_paletteName = readNullTerminatedString(); 481 chunkLength -= metadata.sPLT_paletteName.length() + 1; 482 483 int sampleDepth = stream.readUnsignedByte(); 484 metadata.sPLT_sampleDepth = sampleDepth; 485 486 int numEntries = chunkLength/(4*(sampleDepth/8) + 2); 487 metadata.sPLT_red = new int[numEntries]; 488 metadata.sPLT_green = new int[numEntries]; 489 metadata.sPLT_blue = new int[numEntries]; 490 metadata.sPLT_alpha = new int[numEntries]; 491 metadata.sPLT_frequency = new int[numEntries]; 492 493 if (sampleDepth == 8) { 494 for (int i = 0; i < numEntries; i++) { 495 metadata.sPLT_red[i] = stream.readUnsignedByte(); 496 metadata.sPLT_green[i] = stream.readUnsignedByte(); 497 metadata.sPLT_blue[i] = stream.readUnsignedByte(); 498 metadata.sPLT_alpha[i] = stream.readUnsignedByte(); 499 metadata.sPLT_frequency[i] = stream.readUnsignedShort(); 500 } 501 } else if (sampleDepth == 16) { 502 for (int i = 0; i < numEntries; i++) { 503 metadata.sPLT_red[i] = stream.readUnsignedShort(); 504 metadata.sPLT_green[i] = stream.readUnsignedShort(); 505 metadata.sPLT_blue[i] = stream.readUnsignedShort(); 506 metadata.sPLT_alpha[i] = stream.readUnsignedShort(); 507 metadata.sPLT_frequency[i] = stream.readUnsignedShort(); 508 } 509 } else { 510 throw new IIOException ("sPLT sample depth not 8 or 16!"); 511 } 512 513 metadata.sPLT_present = true; 514 } 515 516 private void parse_sRGB_chunk() throws IOException { 517 metadata.sRGB_renderingIntent = stream.readUnsignedByte(); 518 519 metadata.sRGB_present = true; 520 } 521 522 private void parse_tEXt_chunk(int chunkLength) throws IOException { 523 String keyword = readNullTerminatedString(); 524 metadata.tEXt_keyword.add(keyword); 525 526 byte[] b = new byte[chunkLength - keyword.length() - 1]; 527 stream.readFully(b); 528 metadata.tEXt_text.add(new String (b)); 529 } 530 531 private void parse_tIME_chunk() throws IOException { 532 metadata.tIME_year = stream.readUnsignedShort(); 533 metadata.tIME_month = stream.readUnsignedByte(); 534 metadata.tIME_day = stream.readUnsignedByte(); 535 metadata.tIME_hour = stream.readUnsignedByte(); 536 metadata.tIME_minute = stream.readUnsignedByte(); 537 metadata.tIME_second = stream.readUnsignedByte(); 538 539 metadata.tIME_present = true; 540 } 541 542 private void parse_tRNS_chunk(int chunkLength) throws IOException { 543 int colorType = metadata.IHDR_colorType; 544 if (colorType == PNG_COLOR_PALETTE) { 545 if (!metadata.PLTE_present) { 546 processWarningOccurred( 547 "tRNS chunk without prior PLTE chunk, ignoring it."); 548 return; 549 } 550 551 int maxEntries = metadata.PLTE_red.length; 553 int numEntries = chunkLength; 554 if (numEntries > maxEntries) { 555 processWarningOccurred( 556 "tRNS chunk has more entries than prior PLTE chunk, ignoring extras."); 557 numEntries = maxEntries; 558 } 559 metadata.tRNS_alpha = new byte[numEntries]; 560 metadata.tRNS_colorType = PNG_COLOR_PALETTE; 561 stream.read(metadata.tRNS_alpha, 0, numEntries); 562 stream.skipBytes(chunkLength - numEntries); 563 } else if (colorType == PNG_COLOR_GRAY) { 564 if (chunkLength != 2) { 565 processWarningOccurred( 566 "tRNS chunk for gray image must have length 2, ignoring chunk."); 567 stream.skipBytes(chunkLength); 568 return; 569 } 570 metadata.tRNS_gray = stream.readUnsignedShort(); 571 metadata.tRNS_colorType = PNG_COLOR_GRAY; 572 } else if (colorType == PNG_COLOR_RGB) { 573 if (chunkLength != 6) { 574 processWarningOccurred( 575 "tRNS chunk for RGB image must have length 6, ignoring chunk."); 576 stream.skipBytes(chunkLength); 577 return; 578 } 579 metadata.tRNS_red = stream.readUnsignedShort(); 580 metadata.tRNS_green = stream.readUnsignedShort(); 581 metadata.tRNS_blue = stream.readUnsignedShort(); 582 metadata.tRNS_colorType = PNG_COLOR_RGB; 583 } else { 584 processWarningOccurred( 585 "Gray+Alpha and RGBS images may not have a tRNS chunk, ignoring it."); 586 return; 587 } 588 589 metadata.tRNS_present = true; 590 } 591 592 private static String inflate(byte[] b) throws IOException { 593 InputStream bais = new ByteArrayInputStream (b); 594 InputStream iis = new InflaterInputStream (bais); 595 StringBuffer sb = new StringBuffer (80); 596 int c; 597 while ((c = iis.read()) != -1) { 598 sb.append((char)c); 599 } 600 return sb.toString(); 601 } 602 603 private void parse_zTXt_chunk(int chunkLength) throws IOException { 604 String keyword = readNullTerminatedString(); 605 metadata.zTXt_keyword.add(keyword); 606 607 int method = stream.readUnsignedByte(); 608 metadata.zTXt_compressionMethod.add(new Integer (method)); 609 610 byte[] b = new byte[chunkLength - keyword.length() - 2]; 611 stream.readFully(b); 612 metadata.zTXt_text.add(inflate(b)); 613 } 614 615 private void readMetadata() throws IIOException { 616 if (gotMetadata) { 617 return; 618 } 619 620 readHeader(); 621 622 try { 623 while (true) { 624 int chunkLength = stream.readInt(); 625 int chunkType = stream.readInt(); 626 627 if (chunkType == IDAT_TYPE) { 629 stream.skipBytes(-8); 630 imageStartPosition = stream.getStreamPosition(); 631 break; 632 } 633 634 if (chunkType == PLTE_TYPE) { 635 parse_PLTE_chunk(chunkLength); 636 } else if (chunkType == bKGD_TYPE) { 637 parse_bKGD_chunk(); 638 } else if (chunkType == cHRM_TYPE) { 639 parse_cHRM_chunk(); 640 } else if (chunkType == gAMA_TYPE) { 641 parse_gAMA_chunk(); 642 } else if (chunkType == hIST_TYPE) { 643 parse_hIST_chunk(); 644 } else if (chunkType == iCCP_TYPE) { 645 parse_iCCP_chunk(chunkLength); 646 } else if (chunkType == iTXt_TYPE) { 647 parse_iTXt_chunk(chunkLength); 648 } else if (chunkType == pHYs_TYPE) { 649 parse_pHYs_chunk(); 650 } else if (chunkType == sBIT_TYPE) { 651 parse_sBIT_chunk(); 652 } else if (chunkType == sPLT_TYPE) { 653 parse_sPLT_chunk(chunkLength); 654 } else if (chunkType == sRGB_TYPE) { 655 parse_sRGB_chunk(); 656 } else if (chunkType == tEXt_TYPE) { 657 parse_tEXt_chunk(chunkLength); 658 } else if (chunkType == tIME_TYPE) { 659 parse_tIME_chunk(); 660 } else if (chunkType == tRNS_TYPE) { 661 parse_tRNS_chunk(chunkLength); 662 } else if (chunkType == zTXt_TYPE) { 663 parse_zTXt_chunk(chunkLength); 664 } else { 665 byte[] b = new byte[chunkLength]; 667 stream.readFully(b); 668 669 StringBuffer chunkName = new StringBuffer (4); 670 chunkName.append((char)(chunkType >>> 24)); 671 chunkName.append((char)((chunkType >> 16) & 0xff)); 672 chunkName.append((char)((chunkType >> 8) & 0xff)); 673 chunkName.append((char)(chunkType & 0xff)); 674 675 int ancillaryBit = chunkType >>> 28; 676 if (ancillaryBit == 0) { 677 processWarningOccurred( 678 "Encountered unknown chunk with critical bit set!"); 679 } 680 681 metadata.unknownChunkType.add(chunkName.toString()); 682 metadata.unknownChunkData.add(b); 683 } 684 685 int chunkCRC = stream.readInt(); 686 stream.flushBefore(stream.getStreamPosition()); 687 } 688 } catch (IOException e) { 689 e.printStackTrace(); 690 throw new IIOException ("Error reading PNG metadata", e); 691 } 692 693 gotMetadata = true; 694 } 695 696 698 private static void decodeSubFilter(byte[] curr, int coff, int count, 699 int bpp) { 700 for (int i = bpp; i < count; i++) { 701 int val; 702 703 val = curr[i + coff] & 0xff; 704 val += curr[i + coff - bpp] & 0xff; 705 706 curr[i + coff] = (byte)val; 707 } 708 } 709 710 private static void decodeUpFilter(byte[] curr, int coff, 711 byte[] prev, int poff, 712 int count) { 713 for (int i = 0; i < count; i++) { 714 int raw = curr[i + coff] & 0xff; 715 int prior = prev[i + poff] & 0xff; 716 717 curr[i + coff] = (byte)(raw + prior); 718 } 719 } 720 721 private static void decodeAverageFilter(byte[] curr, int coff, 722 byte[] prev, int poff, 723 int count, int bpp) { 724 int raw, priorPixel, priorRow; 725 726 for (int i = 0; i < bpp; i++) { 727 raw = curr[i + coff] & 0xff; 728 priorRow = prev[i + poff] & 0xff; 729 730 curr[i + coff] = (byte)(raw + priorRow/2); 731 } 732 733 for (int i = bpp; i < count; i++) { 734 raw = curr[i + coff] & 0xff; 735 priorPixel = curr[i + coff - bpp] & 0xff; 736 priorRow = prev[i + poff] & 0xff; 737 738 curr[i + coff] = (byte)(raw + (priorPixel + priorRow)/2); 739 } 740 } 741 742 private static int paethPredictor(int a, int b, int c) { 743 int p = a + b - c; 744 int pa = Math.abs(p - a); 745 int pb = Math.abs(p - b); 746 int pc = Math.abs(p - c); 747 748 if ((pa <= pb) && (pa <= pc)) { 749 return a; 750 } else if (pb <= pc) { 751 return b; 752 } else { 753 return c; 754 } 755 } 756 757 private static void decodePaethFilter(byte[] curr, int coff, 758 byte[] prev, int poff, 759 int count, int bpp) { 760 int raw, priorPixel, priorRow, priorRowPixel; 761 762 for (int i = 0; i < bpp; i++) { 763 raw = curr[i + coff] & 0xff; 764 priorRow = prev[i + poff] & 0xff; 765 766 curr[i + coff] = (byte)(raw + priorRow); 767 } 768 769 for (int i = bpp; i < count; i++) { 770 raw = curr[i + coff] & 0xff; 771 priorPixel = curr[i + coff - bpp] & 0xff; 772 priorRow = prev[i + poff] & 0xff; 773 priorRowPixel = prev[i + poff - bpp] & 0xff; 774 775 curr[i + coff] = (byte)(raw + paethPredictor(priorPixel, 776 priorRow, 777 priorRowPixel)); 778 } 779 } 780 781 private static final int[][] bandOffsets = { 782 null, 783 { 0 }, { 0, 1 }, { 0, 1, 2 }, { 0, 1, 2, 3 } }; 788 789 private WritableRaster createRaster(int width, int height, int bands, 790 int scanlineStride, 791 int bitDepth) { 792 793 DataBuffer dataBuffer; 794 WritableRaster ras = null; 795 Point origin = new Point (0, 0); 796 if ((bitDepth < 8) && (bands == 1)) { 797 dataBuffer = new DataBufferByte (height*scanlineStride); 798 ras = Raster.createPackedRaster(dataBuffer, 799 width, height, 800 bitDepth, 801 origin); 802 } else if (bitDepth <= 8) { 803 dataBuffer = new DataBufferByte (height*scanlineStride); 804 ras = Raster.createInterleavedRaster(dataBuffer, 805 width, height, 806 scanlineStride, 807 bands, 808 bandOffsets[bands], 809 origin); 810 } else { 811 dataBuffer = new DataBufferUShort (height*scanlineStride); 812 ras = Raster.createInterleavedRaster(dataBuffer, 813 width, height, 814 scanlineStride, 815 bands, 816 bandOffsets[bands], 817 origin); 818 } 819 820 return ras; 821 } 822 823 private void skipPass(int passWidth, int passHeight) 824 throws IOException , IIOException { 825 if ((passWidth == 0) || (passHeight == 0)) { 826 return; 827 } 828 829 int inputBands = inputBandsForColorType[metadata.IHDR_colorType]; 830 int bytesPerRow = (inputBands*passWidth*metadata.IHDR_bitDepth + 7)/8; 831 byte[] curr = new byte[bytesPerRow]; 832 833 for (int srcY = 0; srcY < passHeight; srcY++) { 835 int filter = pixelStream.read(); 837 pixelStream.readFully(curr, 0, bytesPerRow); 838 839 if (abortRequested()) { 842 return; 843 } 844 } 845 } 846 847 private static void computeUpdatedPixels(int sourceOffset, 849 int sourceExtent, 850 int destinationOffset, 851 int dstMin, 852 int dstMax, 853 int sourceSubsampling, 854 int passStart, 855 int passExtent, 856 int passPeriod, 857 int[] vals, 858 int offset) { 859 860 889 boolean gotPixel = false; 890 int firstDst = -1; 891 int secondDst = -1; 892 int lastDst = -1; 893 894 for (int i = 0; i < passExtent; i++) { 895 int src = passStart + i*passPeriod; 896 if (src < sourceOffset) { 897 continue; 898 } 899 if ((src - sourceOffset) % sourceSubsampling != 0) { 900 continue; 901 } 902 if (src >= sourceOffset + sourceExtent) { 903 break; 904 } 905 906 int dst = destinationOffset + 907 (src - sourceOffset)/sourceSubsampling; 908 if (dst < dstMin) { 909 continue; 910 } 911 if (dst > dstMax) { 912 break; 913 } 914 915 if (!gotPixel) { 916 firstDst = dst; gotPixel = true; 918 } else if (secondDst == -1) { 919 secondDst = dst; } 921 lastDst = dst; } 923 924 vals[offset] = firstDst; 925 926 if (!gotPixel) { 928 vals[offset + 2] = 0; 929 } else { 930 vals[offset + 2] = lastDst - firstDst + 1; 931 } 932 933 vals[offset + 4] = Math.max(secondDst - firstDst, 1); 935 } 936 937 980 private static int[] computeUpdatedPixels(Rectangle sourceRegion, 981 Point destinationOffset, 982 int dstMinX, 983 int dstMinY, 984 int dstMaxX, 985 int dstMaxY, 986 int sourceXSubsampling, 987 int sourceYSubsampling, 988 int passXStart, 989 int passYStart, 990 int passWidth, 991 int passHeight, 992 int passPeriodX, 993 int passPeriodY) { 994 int[] vals = new int[6]; 995 computeUpdatedPixels(sourceRegion.x, sourceRegion.width, 996 destinationOffset.x, 997 dstMinX, dstMaxX, sourceXSubsampling, 998 passXStart, passWidth, passPeriodX, 999 vals, 0); 1000 computeUpdatedPixels(sourceRegion.y, sourceRegion.height, 1001 destinationOffset.y, 1002 dstMinY, dstMaxY, sourceYSubsampling, 1003 passYStart, passHeight, passPeriodY, 1004 vals, 1); 1005 return vals; 1006 } 1007 1008 private void updateImageProgress(int newPixels) { 1009 pixelsDone += newPixels; 1010 processImageProgress(100.0F*pixelsDone/totalPixels); 1011 } 1012 1013 private void decodePass(int passNum, 1014 int xStart, int yStart, 1015 int xStep, int yStep, 1016 int passWidth, int passHeight) throws IOException { 1017 1018 if ((passWidth == 0) || (passHeight == 0)) { 1019 return; 1020 } 1021 1022 WritableRaster imRas = theImage.getWritableTile(0, 0); 1023 int dstMinX = imRas.getMinX(); 1024 int dstMaxX = dstMinX + imRas.getWidth() - 1; 1025 int dstMinY = imRas.getMinY(); 1026 int dstMaxY = dstMinY + imRas.getHeight() - 1; 1027 1028 int[] vals = computeUpdatedPixels(sourceRegion, 1030 destinationOffset, 1031 dstMinX, dstMinY, 1032 dstMaxX, dstMaxY, 1033 sourceXSubsampling, 1034 sourceYSubsampling, 1035 xStart, yStart, 1036 passWidth, passHeight, 1037 xStep, yStep); 1038 int updateMinX = vals[0]; 1039 int updateMinY = vals[1]; 1040 int updateWidth = vals[2]; 1041 int updateXStep = vals[4]; 1042 int updateYStep = vals[5]; 1043 1044 int bitDepth = metadata.IHDR_bitDepth; 1045 int inputBands = inputBandsForColorType[metadata.IHDR_colorType]; 1046 int bytesPerPixel = (bitDepth == 16) ? 2 : 1; 1047 bytesPerPixel *= inputBands; 1048 1049 int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8; 1050 int eltsPerRow = (bitDepth == 16) ? bytesPerRow/2 : bytesPerRow; 1051 1052 if (updateWidth == 0) { 1054 for (int srcY = 0; srcY < passHeight; srcY++) { 1055 updateImageProgress(passWidth); 1057 pixelStream.skipBytes(1 + bytesPerRow); 1058 } 1059 return; 1060 } 1061 1062 int sourceX = 1067 (updateMinX - destinationOffset.x)*sourceXSubsampling + 1068 sourceRegion.x; 1069 int srcX = (sourceX - xStart)/xStep; 1070 1071 int srcXStep = updateXStep*sourceXSubsampling/xStep; 1073 1074 byte[] byteData = null; 1075 short[] shortData = null; 1076 byte[] curr = new byte[bytesPerRow]; 1077 byte[] prior = new byte[bytesPerRow]; 1078 1079 WritableRaster passRow = createRaster(passWidth, 1, inputBands, 1081 eltsPerRow, 1082 bitDepth); 1083 1084 int[] ps = passRow.getPixel(0, 0, (int[])null); 1086 1087 DataBuffer dataBuffer = passRow.getDataBuffer(); 1088 int type = dataBuffer.getDataType(); 1089 if (type == DataBuffer.TYPE_BYTE) { 1090 byteData = ((DataBufferByte )dataBuffer).getData(); 1091 } else { 1092 shortData = ((DataBufferUShort )dataBuffer).getData(); 1093 } 1094 1095 processPassStarted(theImage, 1096 passNum, 1097 sourceMinProgressivePass, 1098 sourceMaxProgressivePass, 1099 updateMinX, updateMinY, 1100 updateXStep, updateYStep, 1101 destinationBands); 1102 1103 if (sourceBands != null) { 1105 passRow = passRow.createWritableChild(0, 0, 1106 passRow.getWidth(), 1, 1107 0, 0, 1108 sourceBands); 1109 } 1110 if (destinationBands != null) { 1111 imRas = imRas.createWritableChild(0, 0, 1112 imRas.getWidth(), 1113 imRas.getHeight(), 1114 0, 0, 1115 destinationBands); 1116 } 1117 1118 boolean adjustBitDepths = false; 1121 int[] outputSampleSize = imRas.getSampleModel().getSampleSize(); 1122 int numBands = outputSampleSize.length; 1123 for (int b = 0; b < numBands; b++) { 1124 if (outputSampleSize[b] != bitDepth) { 1125 adjustBitDepths = true; 1126 break; 1127 } 1128 } 1129 1130 int[][] scale = null; 1133 if (adjustBitDepths) { 1134 int maxInSample = (1 << bitDepth) - 1; 1135 int halfMaxInSample = maxInSample/2; 1136 scale = new int[numBands][]; 1137 for (int b = 0; b < numBands; b++) { 1138 int maxOutSample = (1 << outputSampleSize[b]) - 1; 1139 scale[b] = new int[maxInSample + 1]; 1140 for (int s = 0; s <= maxInSample; s++) { 1141 scale[b][s] = 1142 (s*maxOutSample + halfMaxInSample)/maxInSample; 1143 } 1144 } 1145 } 1146 1147 boolean useSetRect = srcXStep == 1 && 1150 updateXStep == 1 && 1151 !adjustBitDepths; 1152 if (useSetRect) { 1153 passRow = passRow.createWritableChild(srcX, 0, 1154 updateWidth, 1, 1155 0, 0, 1156 null); 1157 } 1158 1159 for (int srcY = 0; srcY < passHeight; srcY++) { 1161 updateImageProgress(passWidth); 1163 1164 int filter = pixelStream.read(); 1166 try { 1167 byte[] tmp = prior; 1169 prior = curr; 1170 curr = tmp; 1171 1172 pixelStream.readFully(curr, 0, bytesPerRow); 1173 } catch (java.util.zip.ZipException ze) { 1174 throw ze; 1176 } 1177 1178 switch (filter) { 1179 case PNG_FILTER_NONE: 1180 break; 1181 case PNG_FILTER_SUB: 1182 decodeSubFilter(curr, 0, bytesPerRow, bytesPerPixel); 1183 break; 1184 case PNG_FILTER_UP: 1185 decodeUpFilter(curr, 0, prior, 0, bytesPerRow); 1186 break; 1187 case PNG_FILTER_AVERAGE: 1188 decodeAverageFilter(curr, 0, prior, 0, bytesPerRow, 1189 bytesPerPixel); 1190 break; 1191 case PNG_FILTER_PAETH: 1192 decodePaethFilter(curr, 0, prior, 0, bytesPerRow, 1193 bytesPerPixel); 1194 break; 1195 default: 1196 throw new IIOException ("Unknown row filter type (= " + 1197 filter + ")!"); 1198 } 1199 1200 if (bitDepth < 16) { 1202 System.arraycopy(curr, 0, byteData, 0, bytesPerRow); 1203 } else { 1204 int idx = 0; 1205 for (int j = 0; j < eltsPerRow; j++) { 1206 shortData[j] = 1207 (short)((curr[idx] << 8) | (curr[idx + 1] & 0xff)); 1208 idx += 2; 1209 } 1210 } 1211 1212 int sourceY = srcY*yStep + yStart; 1214 if ((sourceY >= sourceRegion.y) && 1215 (sourceY < sourceRegion.y + sourceRegion.height) && 1216 (((sourceY - sourceRegion.y) % 1217 sourceYSubsampling) == 0)) { 1218 1219 int dstY = destinationOffset.y + 1220 (sourceY - sourceRegion.y)/sourceYSubsampling; 1221 if (dstY < dstMinY) { 1222 continue; 1223 } 1224 if (dstY > dstMaxY) { 1225 break; 1226 } 1227 1228 if (useSetRect) { 1229 imRas.setRect(updateMinX, dstY, passRow); 1230 } else { 1231 int newSrcX = srcX; 1232 1233 for (int dstX = updateMinX; 1234 dstX < updateMinX + updateWidth; 1235 dstX += updateXStep) { 1236 1237 passRow.getPixel(newSrcX, 0, ps); 1238 if (adjustBitDepths) { 1239 for (int b = 0; b < numBands; b++) { 1240 ps[b] = scale[b][ps[b]]; 1241 } 1242 } 1243 imRas.setPixel(dstX, dstY, ps); 1244 newSrcX += srcXStep; 1245 } 1246 } 1247 1248 processImageUpdate(theImage, 1249 updateMinX, dstY, 1250 updateWidth, 1, 1251 updateXStep, updateYStep, 1252 destinationBands); 1253 1254 if (abortRequested()) { 1257 return; 1258 } 1259 } 1260 } 1261 1262 processPassComplete(theImage); 1263 } 1264 1265 private void decodeImage() 1266 throws IOException , IIOException { 1267 int width = metadata.IHDR_width; 1268 int height = metadata.IHDR_height; 1269 1270 this.pixelsDone = 0; 1271 this.totalPixels = width*height; 1272 1273 clearAbortRequest(); 1274 1275 if (metadata.IHDR_interlaceMethod == 0) { 1276 decodePass(0, 0, 0, 1, 1, width, height); 1277 } else { 1278 for (int i = 0; i <= sourceMaxProgressivePass; i++) { 1279 int XOffset = adam7XOffset[i]; 1280 int YOffset = adam7YOffset[i]; 1281 int XSubsampling = adam7XSubsampling[i]; 1282 int YSubsampling = adam7YSubsampling[i]; 1283 int xbump = adam7XSubsampling[i + 1] - 1; 1284 int ybump = adam7YSubsampling[i + 1] - 1; 1285 1286 if (i >= sourceMinProgressivePass) { 1287 decodePass(i, 1288 XOffset, 1289 YOffset, 1290 XSubsampling, 1291 YSubsampling, 1292 (width + xbump)/XSubsampling, 1293 (height + ybump)/YSubsampling); 1294 } else { 1295 skipPass((width + xbump)/XSubsampling, 1296 (height + ybump)/YSubsampling); 1297 } 1298 1299 if (abortRequested()) { 1302 return; 1303 } 1304 } 1305 } 1306 } 1307 1308 private void readImage(ImageReadParam param) throws IIOException { 1309 readMetadata(); 1310 1311 int width = metadata.IHDR_width; 1312 int height = metadata.IHDR_height; 1313 1314 sourceRegion = getSourceRegion(param, width, height); 1316 sourceXSubsampling = 1; 1317 sourceYSubsampling = 1; 1318 sourceMinProgressivePass = 0; 1319 sourceMaxProgressivePass = 6; 1320 sourceBands = null; 1321 destinationBands = null; 1322 destinationOffset = new Point (0, 0); 1323 1324 if (param != null) { 1326 sourceXSubsampling = param.getSourceXSubsampling(); 1327 sourceYSubsampling = param.getSourceYSubsampling(); 1328 1329 sourceMinProgressivePass = 1330 Math.max(param.getSourceMinProgressivePass(), 0); 1331 sourceMaxProgressivePass = 1332 Math.min(param.getSourceMaxProgressivePass(), 6); 1333 1334 sourceBands = param.getSourceBands(); 1335 destinationBands = param.getDestinationBands(); 1336 destinationOffset = param.getDestinationOffset(); 1337 } 1338 1339 try { 1340 stream.seek(imageStartPosition); 1341 1342 Enumeration e = new PNGImageDataEnumeration(stream); 1343 InputStream is = new SequenceInputStream (e); 1344 is = new InflaterInputStream (is, new Inflater ()); 1345 is = new BufferedInputStream (is); 1346 this.pixelStream = new DataInputStream (is); 1347 1348 theImage = getDestination(param, 1349 getImageTypes(0), 1350 width, 1351 height); 1352 1353 int colorType = metadata.IHDR_colorType; 1357 checkReadParamBandSettings(param, 1358 inputBandsForColorType[colorType], 1359 theImage.getSampleModel().getNumBands()); 1360 1361 processImageStarted(0); 1362 decodeImage(); 1363 if (abortRequested()) { 1364 processReadAborted(); 1365 } else { 1366 processImageComplete(); 1367 } 1368 } catch (IOException e) { 1369 e.printStackTrace(); 1370 throw new IIOException ("Error reading PNG image data", e); 1371 } 1372 } 1373 1374 public int getNumImages(boolean allowSearch) throws IIOException { 1375 if (stream == null) { 1376 throw new IllegalStateException ("No input source set!"); 1377 } 1378 if (seekForwardOnly && allowSearch) { 1379 throw new IllegalStateException 1380 ("seekForwardOnly and allowSearch can't both be true!"); 1381 } 1382 return 1; 1383 } 1384 1385 public int getWidth(int imageIndex) throws IIOException { 1386 readHeader(); 1387 1388 return metadata.IHDR_width; 1389 } 1390 1391 public int getHeight(int imageIndex) throws IIOException { 1392 readHeader(); 1393 1394 return metadata.IHDR_height; 1395 } 1396 1397 public Iterator getImageTypes(int imageIndex) throws IIOException { 1398 if (imageIndex != 0) { 1399 throw new IndexOutOfBoundsException ("imageIndex != 0!"); 1400 } 1401 1402 readHeader(); 1403 1404 ArrayList l = new ArrayList (1); ColorSpace rgb; 1406 ColorSpace gray; 1407 int[] bandOffsets; 1408 1409 int bitDepth = metadata.IHDR_bitDepth; 1410 int colorType = metadata.IHDR_colorType; 1411 1412 int dataType; 1413 if (bitDepth <= 8) { 1414 dataType = DataBuffer.TYPE_BYTE; 1415 } else { 1416 dataType = DataBuffer.TYPE_USHORT; 1417 } 1418 1419 switch (colorType) { 1420 case PNG_COLOR_GRAY: 1421 l.add(ImageTypeSpecifier.createGrayscale(bitDepth, 1423 dataType, 1424 false)); 1425 break; 1426 1427 case PNG_COLOR_RGB: 1428 rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB); 1430 bandOffsets = new int[3]; 1431 bandOffsets[0] = 0; 1432 bandOffsets[1] = 1; 1433 bandOffsets[2] = 2; 1434 l.add(ImageTypeSpecifier.createInterleaved(rgb, 1435 bandOffsets, 1436 dataType, 1437 false, 1438 false)); 1439 break; 1440 1441 case PNG_COLOR_PALETTE: 1442 readMetadata(); 1444 1467 1468 int plength = 1 << bitDepth; 1469 1470 byte[] red = metadata.PLTE_red; 1471 byte[] green = metadata.PLTE_green; 1472 byte[] blue = metadata.PLTE_blue; 1473 1474 if (metadata.PLTE_red.length < plength) { 1475 red = new byte[plength]; 1476 System.arraycopy(metadata.PLTE_red, 0, red, 0, 1477 metadata.PLTE_red.length); 1478 Arrays.fill(red, metadata.PLTE_red.length, plength, 1479 metadata.PLTE_red[metadata.PLTE_red.length - 1]); 1480 1481 green = new byte[plength]; 1482 System.arraycopy(metadata.PLTE_green, 0, green, 0, 1483 Math.min(metadata.PLTE_green.length, plength)); 1484 1485 Arrays.fill(green, metadata.PLTE_green.length, plength, 1486 metadata.PLTE_green[metadata.PLTE_green.length - 1]); 1487 1488 blue = new byte[plength]; 1489 System.arraycopy(metadata.PLTE_blue, 0, blue, 0, 1490 Math.min(metadata.PLTE_blue.length, plength)); 1491 Arrays.fill(blue, metadata.PLTE_blue.length, plength, 1492 metadata.PLTE_blue[metadata.PLTE_blue.length - 1]); 1493 1494 } 1495 1496 1497 byte[] alpha = null; 1501 if (metadata.tRNS_present && (metadata.tRNS_alpha != null)) { 1502 if (metadata.tRNS_alpha.length == red.length) { 1503 alpha = metadata.tRNS_alpha; 1504 } else { 1505 alpha = new byte[red.length]; 1506 System.arraycopy(metadata.tRNS_alpha, 0, alpha, 0, 1507 Math.min(metadata.tRNS_alpha.length, red.length)); 1508 Arrays.fill(alpha, 1509 metadata.tRNS_alpha.length, 1510 red.length, (byte)255); 1511 } 1512 } 1513 1514 l.add(ImageTypeSpecifier.createIndexed(red, green, 1515 blue, alpha, 1516 bitDepth, 1517 DataBuffer.TYPE_BYTE)); 1518 break; 1519 1520 case PNG_COLOR_GRAY_ALPHA: 1521 gray = ColorSpace.getInstance(ColorSpace.CS_GRAY); 1523 bandOffsets = new int[2]; 1524 bandOffsets[0] = 0; 1525 bandOffsets[1] = 1; 1526 l.add(ImageTypeSpecifier.createInterleaved(gray, 1527 bandOffsets, 1528 dataType, 1529 true, 1530 false)); 1531 break; 1532 1533 case PNG_COLOR_RGB_ALPHA: 1534 rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB); 1536 bandOffsets = new int[4]; 1537 bandOffsets[0] = 0; 1538 bandOffsets[1] = 1; 1539 bandOffsets[2] = 2; 1540 bandOffsets[3] = 3; 1541 1542 l.add(ImageTypeSpecifier.createInterleaved(rgb, 1543 bandOffsets, 1544 dataType, 1545 true, 1546 false)); 1547 break; 1548 1549 default: 1550 break; 1551 } 1552 1553 return l.iterator(); 1554 } 1555 1556 public ImageReadParam getDefaultReadParam() { 1557 return new ImageReadParam (); 1558 } 1559 1560 public IIOMetadata getStreamMetadata() 1561 throws IIOException { 1562 return null; 1563 } 1564 1565 public IIOMetadata getImageMetadata(int imageIndex) throws IIOException { 1566 if (imageIndex != 0) { 1567 throw new IndexOutOfBoundsException ("imageIndex != 0!"); 1568 } 1569 readMetadata(); 1570 return metadata; 1571 } 1572 1573 public BufferedImage read(int imageIndex, ImageReadParam param) 1574 throws IIOException { 1575 if (imageIndex != 0) { 1576 throw new IndexOutOfBoundsException ("imageIndex != 0!"); 1577 } 1578 1579 readImage(param); 1580 return theImage; 1581 } 1582 1583 public void reset() { 1584 super.reset(); 1585 resetStreamSettings(); 1586 } 1587 1588 private void resetStreamSettings() { 1589 gotHeader = false; 1590 gotMetadata = false; 1591 metadata = null; 1592 pixelStream = null; 1593 } 1594} 1595 | Popular Tags |