1 7 8 package com.sun.imageio.plugins.png; 9 10 import java.awt.Rectangle ; 11 import java.awt.image.ColorModel ; 12 import java.awt.image.IndexColorModel ; 13 import java.awt.image.Raster ; 14 import java.awt.image.WritableRaster ; 15 import java.awt.image.RenderedImage ; 16 import java.awt.image.SampleModel ; 17 import java.io.ByteArrayOutputStream ; 18 import java.io.DataOutput ; 19 import java.io.IOException ; 20 import java.io.OutputStream ; 21 import java.util.Iterator ; 22 import java.util.Locale ; 23 import java.util.zip.Deflater ; 24 import java.util.zip.DeflaterOutputStream ; 25 import javax.imageio.IIOException ; 26 import javax.imageio.IIOImage ; 27 import javax.imageio.ImageTypeSpecifier ; 28 import javax.imageio.ImageWriteParam ; 29 import javax.imageio.ImageWriter ; 30 import javax.imageio.metadata.IIOMetadata ; 31 import javax.imageio.metadata.IIOMetadata ; 32 import javax.imageio.spi.ImageWriterSpi ; 33 import javax.imageio.stream.ImageOutputStream ; 34 import javax.imageio.stream.ImageOutputStreamImpl ; 35 36 class CRC { 37 38 private static int[] crcTable = new int[256]; 39 private int crc = 0xffffffff; 40 41 static { 42 for (int n = 0; n < 256; n++) { 44 int c = n; 45 for (int k = 0; k < 8; k++) { 46 if ((c & 1) == 1) { 47 c = 0xedb88320 ^ (c >>> 1); 48 } else { 49 c >>>= 1; 50 } 51 52 crcTable[n] = c; 53 } 54 } 55 } 56 57 public CRC() {} 58 59 public void reset() { 60 crc = 0xffffffff; 61 } 62 63 public void update(byte[] data, int off, int len) { 64 for (int n = 0; n < len; n++) { 65 crc = crcTable[(crc ^ data[off + n]) & 0xff] ^ (crc >>> 8); 66 } 67 } 68 69 public void update(int data) { 70 crc = crcTable[(crc ^ data) & 0xff] ^ (crc >>> 8); 71 } 72 73 public int getValue() { 74 return crc ^ 0xffffffff; 75 } 76 } 77 78 79 class ChunkStream extends ImageOutputStreamImpl { 80 81 private ImageOutputStream stream; 82 private long startPos; 83 private CRC crc = new CRC(); 84 85 public ChunkStream(int type, ImageOutputStream stream) throws IOException { 86 this.stream = stream; 87 this.startPos = stream.getStreamPosition(); 88 89 stream.writeInt(-1); writeInt(type); 91 } 92 93 public int read() throws IOException { 94 throw new RuntimeException ("Method not available"); 95 } 96 97 public int read(byte[] b, int off, int len) throws IOException { 98 throw new RuntimeException ("Method not available"); 99 } 100 101 public void write(byte[] b, int off, int len) throws IOException { 102 crc.update(b, off, len); 103 stream.write(b, off, len); 104 } 105 106 public void write(int b) throws IOException { 107 crc.update(b); 108 stream.write(b); 109 } 110 111 public void finish() throws IOException { 112 stream.writeInt(crc.getValue()); 114 115 long pos = stream.getStreamPosition(); 117 stream.seek(startPos); 118 stream.writeInt((int)(pos - startPos) - 12); 119 120 stream.seek(pos); 122 stream.flushBefore(pos); 123 } 124 } 125 126 class IDATOutputStream extends ImageOutputStreamImpl { 129 130 private static byte[] chunkType = { 131 (byte)'I', (byte)'D', (byte)'A', (byte)'T' 132 }; 133 134 private ImageOutputStream stream; 135 private int chunkLength; 136 private long startPos; 137 private CRC crc = new CRC(); 138 139 Deflater def = new Deflater (Deflater.BEST_COMPRESSION); 140 byte[] buf = new byte[512]; 141 142 private int bytesRemaining; 143 144 public IDATOutputStream(ImageOutputStream stream, int chunkLength) 145 throws IOException { 146 this.stream = stream; 147 this.chunkLength = chunkLength; 148 startChunk(); 149 } 150 151 private void startChunk() throws IOException { 152 crc.reset(); 153 this.startPos = stream.getStreamPosition(); 154 stream.writeInt(-1); 156 crc.update(chunkType, 0, 4); 157 stream.write(chunkType, 0, 4); 158 159 this.bytesRemaining = chunkLength; 160 } 161 162 private void finishChunk() throws IOException { 163 stream.writeInt(crc.getValue()); 165 166 long pos = stream.getStreamPosition(); 168 stream.seek(startPos); 169 stream.writeInt((int)(pos - startPos) - 12); 170 171 stream.seek(pos); 173 stream.flushBefore(pos); 174 } 175 176 public int read() throws IOException { 177 throw new RuntimeException ("Method not available"); 178 } 179 180 public int read(byte[] b, int off, int len) throws IOException { 181 throw new RuntimeException ("Method not available"); 182 } 183 184 public void write(byte[] b, int off, int len) throws IOException { 185 if (len == 0) { 186 return; 187 } 188 189 if (!def.finished()) { 190 def.setInput(b, off, len); 191 while (!def.needsInput()) { 192 deflate(); 193 } 194 } 195 } 196 197 public void deflate() throws IOException { 198 int len = def.deflate(buf, 0, buf.length); 199 int off = 0; 200 201 while (len > 0) { 202 if (bytesRemaining == 0) { 203 finishChunk(); 204 startChunk(); 205 } 206 207 int nbytes = Math.min(len, bytesRemaining); 208 crc.update(buf, off, nbytes); 209 stream.write(buf, off, nbytes); 210 211 off += nbytes; 212 len -= nbytes; 213 bytesRemaining -= nbytes; 214 } 215 } 216 217 public void write(int b) throws IOException { 218 byte[] wbuf = new byte[1]; 219 wbuf[0] = (byte)b; 220 write(wbuf, 0, 1); 221 } 222 223 public void finish() throws IOException { 224 if (!def.finished()) { 225 def.finish(); 226 while (!def.finished()) { 227 deflate(); 228 } 229 } 230 finishChunk(); 231 } 232 } 233 234 235 class PNGImageWriteParam extends ImageWriteParam { 236 237 public PNGImageWriteParam(Locale locale) { 238 super(); 239 this.canWriteProgressive = true; 240 this.locale = locale; 241 } 242 } 243 244 247 public class PNGImageWriter extends ImageWriter { 248 249 ImageOutputStream stream = null; 250 251 PNGMetadata metadata = null; 252 253 int sourceXOffset = 0; 255 int sourceYOffset = 0; 256 int sourceWidth = 0; 257 int sourceHeight = 0; 258 int[] sourceBands = null; 259 int periodX = 1; 260 int periodY = 1; 261 262 int numBands; 263 int bpp; 264 265 RowFilter rowFilter = new RowFilter(); 266 byte[] prevRow = null; 267 byte[] currRow = null; 268 byte[][] filteredRows = null; 269 270 int[] sampleSize = null; int scalingBitDepth = -1; 281 byte[][] scale = null; byte[] scale0 = null; 285 byte[][] scaleh = null; byte[][] scalel = null; 289 int totalPixels; int pixelsDone; 292 public PNGImageWriter(ImageWriterSpi originatingProvider) { 293 super(originatingProvider); 294 } 295 296 public void setOutput(Object output) { 297 super.setOutput(output); 298 if (output != null) { 299 if (!(output instanceof ImageOutputStream )) { 300 throw new IllegalArgumentException ("output not an ImageOutputStream!"); 301 } 302 this.stream = (ImageOutputStream )output; 303 } else { 304 this.stream = null; 305 } 306 } 307 308 private static int[] allowedProgressivePasses = { 1, 7 }; 309 310 public ImageWriteParam getDefaultWriteParam() { 311 return new PNGImageWriteParam(getLocale()); 312 } 313 314 public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) { 315 return null; 316 } 317 318 public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, 319 ImageWriteParam param) { 320 PNGMetadata m = new PNGMetadata(); 321 m.initialize(imageType, imageType.getSampleModel().getNumBands()); 322 return m; 323 } 324 325 public IIOMetadata convertStreamMetadata(IIOMetadata inData, 326 ImageWriteParam param) { 327 return null; 328 } 329 330 public IIOMetadata convertImageMetadata(IIOMetadata inData, 331 ImageTypeSpecifier imageType, 332 ImageWriteParam param) { 333 if (inData instanceof PNGMetadata) { 335 return (PNGMetadata)((PNGMetadata)inData).clone(); 336 } else { 337 return new PNGMetadata(inData); 338 } 339 } 340 341 private void write_magic() throws IOException { 342 byte[] magic = { (byte)137, 80, 78, 71, 13, 10, 26, 10 }; 344 stream.write(magic); 345 } 346 347 private void write_IHDR() throws IOException { 348 ChunkStream cs = new ChunkStream(PNGImageReader.IHDR_TYPE, stream); 350 cs.writeInt(metadata.IHDR_width); 351 cs.writeInt(metadata.IHDR_height); 352 cs.writeByte(metadata.IHDR_bitDepth); 353 cs.writeByte(metadata.IHDR_colorType); 354 if (metadata.IHDR_compressionMethod != 0) { 355 throw new IIOException ( 356 "Only compression method 0 is defined in PNG 1.1"); 357 } 358 cs.writeByte(metadata.IHDR_compressionMethod); 359 if (metadata.IHDR_filterMethod != 0) { 360 throw new IIOException ( 361 "Only filter method 0 is defined in PNG 1.1"); 362 } 363 cs.writeByte(metadata.IHDR_filterMethod); 364 if (metadata.IHDR_interlaceMethod < 0 || 365 metadata.IHDR_interlaceMethod > 1) { 366 throw new IIOException ( 367 "Only interlace methods 0 (node) and 1 (adam7) are defined in PNG 1.1"); 368 } 369 cs.writeByte(metadata.IHDR_interlaceMethod); 370 cs.finish(); 371 } 372 373 private void write_cHRM() throws IOException { 374 if (metadata.cHRM_present) { 375 ChunkStream cs = new ChunkStream(PNGImageReader.cHRM_TYPE, stream); 376 cs.writeInt(metadata.cHRM_whitePointX); 377 cs.writeInt(metadata.cHRM_whitePointY); 378 cs.writeInt(metadata.cHRM_redX); 379 cs.writeInt(metadata.cHRM_redY); 380 cs.writeInt(metadata.cHRM_greenX); 381 cs.writeInt(metadata.cHRM_greenY); 382 cs.writeInt(metadata.cHRM_blueX); 383 cs.writeInt(metadata.cHRM_blueY); 384 cs.finish(); 385 } 386 } 387 388 private void write_gAMA() throws IOException { 389 if (metadata.gAMA_present) { 390 ChunkStream cs = new ChunkStream(PNGImageReader.gAMA_TYPE, stream); 391 cs.writeInt(metadata.gAMA_gamma); 392 cs.finish(); 393 } 394 } 395 396 private void write_iCCP() throws IOException { 397 if (metadata.iCCP_present) { 398 ChunkStream cs = new ChunkStream(PNGImageReader.iCCP_TYPE, stream); 399 cs.writeBytes(metadata.iCCP_profileName); 400 cs.writeByte(0); 402 cs.writeByte(metadata.iCCP_compressionMethod); 403 cs.write(metadata.iCCP_compressedProfile); 404 cs.finish(); 405 } 406 } 407 408 private void write_sBIT() throws IOException { 409 if (metadata.sBIT_present) { 410 ChunkStream cs = new ChunkStream(PNGImageReader.sBIT_TYPE, stream); 411 int colorType = metadata.IHDR_colorType; 412 if (metadata.sBIT_colorType != colorType) { 413 processWarningOccurred(0, 414 "sBIT metadata has wrong color type.\n" + 415 "The chunk will not be written."); 416 return; 417 } 418 419 if (colorType == PNGImageReader.PNG_COLOR_GRAY || 420 colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) { 421 cs.writeByte(metadata.sBIT_grayBits); 422 } else if (colorType == PNGImageReader.PNG_COLOR_RGB || 423 colorType == PNGImageReader.PNG_COLOR_PALETTE || 424 colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) { 425 cs.writeByte(metadata.sBIT_redBits); 426 cs.writeByte(metadata.sBIT_greenBits); 427 cs.writeByte(metadata.sBIT_blueBits); 428 } 429 430 if (colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA || 431 colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) { 432 cs.writeByte(metadata.sBIT_alphaBits); 433 } 434 cs.finish(); 435 } 436 } 437 438 private void write_sRGB() throws IOException { 439 if (metadata.sRGB_present) { 440 ChunkStream cs = new ChunkStream(PNGImageReader.sRGB_TYPE, stream); 441 cs.writeByte(metadata.sRGB_renderingIntent); 442 cs.finish(); 443 } 444 } 445 446 private void write_PLTE() throws IOException { 447 if (metadata.PLTE_present) { 448 if (metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY || 449 metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) { 450 452 processWarningOccurred(0, 453 "A PLTE chunk may not appear in a gray or gray alpha image.\n" + 454 "The chunk will not be written"); 455 return; 456 } 457 458 ChunkStream cs = new ChunkStream(PNGImageReader.PLTE_TYPE, stream); 459 460 int numEntries = metadata.PLTE_red.length; 461 byte[] palette = new byte[numEntries*3]; 462 int index = 0; 463 for (int i = 0; i < numEntries; i++) { 464 palette[index++] = metadata.PLTE_red[i]; 465 palette[index++] = metadata.PLTE_green[i]; 466 palette[index++] = metadata.PLTE_blue[i]; 467 } 468 469 cs.write(palette); 470 cs.finish(); 471 } 472 } 473 474 private void write_hIST() throws IOException , IIOException { 475 if (metadata.hIST_present) { 476 ChunkStream cs = new ChunkStream(PNGImageReader.hIST_TYPE, stream); 477 478 if (!metadata.PLTE_present) { 479 throw new IIOException ("hIST chunk without PLTE chunk!"); 480 } 481 482 cs.writeChars(metadata.hIST_histogram, 483 0, metadata.hIST_histogram.length); 484 cs.finish(); 485 } 486 } 487 488 private void write_tRNS() throws IOException , IIOException { 489 if (metadata.tRNS_present) { 490 ChunkStream cs = new ChunkStream(PNGImageReader.tRNS_TYPE, stream); 491 int colorType = metadata.IHDR_colorType; 492 int chunkType = metadata.tRNS_colorType; 493 494 int chunkRed = metadata.tRNS_red; 497 int chunkGreen = metadata.tRNS_green; 498 int chunkBlue = metadata.tRNS_blue; 499 if (colorType == PNGImageReader.PNG_COLOR_RGB && 500 chunkType == PNGImageReader.PNG_COLOR_GRAY) { 501 chunkType = colorType; 502 chunkRed = chunkGreen = chunkBlue = 503 metadata.tRNS_gray; 504 } 505 506 if (chunkType != colorType) { 507 processWarningOccurred(0, 508 "tRNS metadata has incompatible color type.\n" + 509 "The chunk will not be written."); 510 return; 511 } 512 513 if (colorType == PNGImageReader.PNG_COLOR_PALETTE) { 514 if (!metadata.PLTE_present) { 515 throw new IIOException ("tRNS chunk without PLTE chunk!"); 516 } 517 cs.write(metadata.tRNS_alpha); 518 } else if (colorType == PNGImageReader.PNG_COLOR_GRAY) { 519 cs.writeShort(metadata.tRNS_gray); 520 } else if (colorType == PNGImageReader.PNG_COLOR_RGB) { 521 cs.writeShort(chunkRed); 522 cs.writeShort(chunkGreen); 523 cs.writeShort(chunkBlue); 524 } else { 525 throw new IIOException ("tRNS chunk for color type 4 or 6!"); 526 } 527 cs.finish(); 528 } 529 } 530 531 private void write_bKGD() throws IOException { 532 if (metadata.bKGD_present) { 533 ChunkStream cs = new ChunkStream(PNGImageReader.bKGD_TYPE, stream); 534 int colorType = metadata.IHDR_colorType & 0x3; 535 int chunkType = metadata.bKGD_colorType; 536 537 int chunkRed = metadata.bKGD_red; 540 int chunkGreen = metadata.bKGD_red; 541 int chunkBlue = metadata.bKGD_red; 542 if (colorType == PNGImageReader.PNG_COLOR_RGB && 543 chunkType == PNGImageReader.PNG_COLOR_GRAY) { 544 chunkType = colorType; 546 chunkRed = chunkGreen = chunkBlue = 547 metadata.bKGD_gray; 548 } 549 550 if (chunkType != colorType) { 552 processWarningOccurred(0, 553 "bKGD metadata has incompatible color type.\n" + 554 "The chunk will not be written."); 555 return; 556 } 557 558 if (colorType == PNGImageReader.PNG_COLOR_PALETTE) { 559 cs.writeByte(metadata.bKGD_index); 560 } else if (colorType == PNGImageReader.PNG_COLOR_GRAY || 561 colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) { 562 cs.writeShort(metadata.bKGD_gray); 563 } else { cs.writeShort(chunkRed); 566 cs.writeShort(chunkGreen); 567 cs.writeShort(chunkBlue); 568 } 569 cs.finish(); 570 } 571 } 572 573 private void write_pHYs() throws IOException { 574 if (metadata.pHYs_present) { 575 ChunkStream cs = new ChunkStream(PNGImageReader.pHYs_TYPE, stream); 576 cs.writeInt(metadata.pHYs_pixelsPerUnitXAxis); 577 cs.writeInt(metadata.pHYs_pixelsPerUnitYAxis); 578 cs.writeByte(metadata.pHYs_unitSpecifier); 579 cs.finish(); 580 } 581 } 582 583 private void write_sPLT() throws IOException { 584 if (metadata.sPLT_present) { 585 ChunkStream cs = new ChunkStream(PNGImageReader.sPLT_TYPE, stream); 586 587 cs.writeBytes(metadata.sPLT_paletteName); 588 cs.writeByte(0); 590 cs.writeByte(metadata.sPLT_sampleDepth); 591 int numEntries = metadata.sPLT_red.length; 592 593 if (metadata.sPLT_sampleDepth == 8) { 594 for (int i = 0; i < numEntries; i++) { 595 cs.writeByte(metadata.sPLT_red[i]); 596 cs.writeByte(metadata.sPLT_green[i]); 597 cs.writeByte(metadata.sPLT_blue[i]); 598 cs.writeByte(metadata.sPLT_alpha[i]); 599 cs.writeShort(metadata.sPLT_frequency[i]); 600 } 601 } else { for (int i = 0; i < numEntries; i++) { 603 cs.writeShort(metadata.sPLT_red[i]); 604 cs.writeShort(metadata.sPLT_green[i]); 605 cs.writeShort(metadata.sPLT_blue[i]); 606 cs.writeShort(metadata.sPLT_alpha[i]); 607 cs.writeShort(metadata.sPLT_frequency[i]); 608 } 609 } 610 cs.finish(); 611 } 612 } 613 614 private void write_tIME() throws IOException { 615 if (metadata.tIME_present) { 616 ChunkStream cs = new ChunkStream(PNGImageReader.tIME_TYPE, stream); 617 cs.writeShort(metadata.tIME_year); 618 cs.writeByte(metadata.tIME_month); 619 cs.writeByte(metadata.tIME_day); 620 cs.writeByte(metadata.tIME_hour); 621 cs.writeByte(metadata.tIME_minute); 622 cs.writeByte(metadata.tIME_second); 623 cs.finish(); 624 } 625 } 626 627 private void write_tEXt() throws IOException { 628 Iterator keywordIter = metadata.tEXt_keyword.iterator(); 629 Iterator textIter = metadata.tEXt_text.iterator(); 630 631 while (keywordIter.hasNext()) { 632 ChunkStream cs = new ChunkStream(PNGImageReader.tEXt_TYPE, stream); 633 String keyword = (String )keywordIter.next(); 634 cs.writeBytes(keyword); 635 cs.writeByte(0); 636 637 String text = (String )textIter.next(); 638 cs.writeBytes(text); 639 cs.finish(); 640 } 641 } 642 643 private byte[] deflate(String s) throws IOException { 644 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 645 DeflaterOutputStream dos = new DeflaterOutputStream (baos); 646 647 int len = s.length(); 648 for (int i = 0; i < len; i++) { 649 dos.write((int)s.charAt(i)); 650 } 651 dos.close(); 652 653 return baos.toByteArray(); 654 } 655 656 private void write_iTXt() throws IOException { 657 Iterator keywordIter = metadata.iTXt_keyword.iterator(); 658 Iterator flagIter = metadata.iTXt_compressionFlag.iterator(); 659 Iterator methodIter = metadata.iTXt_compressionMethod.iterator(); 660 Iterator languageIter = metadata.iTXt_languageTag.iterator(); 661 Iterator translatedKeywordIter = 662 metadata.iTXt_translatedKeyword.iterator(); 663 Iterator textIter = metadata.iTXt_text.iterator(); 664 665 while (keywordIter.hasNext()) { 666 ChunkStream cs = new ChunkStream(PNGImageReader.iTXt_TYPE, stream); 667 String keyword = (String )keywordIter.next(); 668 cs.writeBytes(keyword); 669 cs.writeByte(0); 670 671 int flag = ((Integer )flagIter.next()).intValue(); 672 cs.writeByte(flag); 673 int method = ((Integer )methodIter.next()).intValue(); 674 cs.writeByte(method); 675 676 String languageTag = (String )languageIter.next(); 677 cs.writeBytes(languageTag); 678 cs.writeByte(0); 679 680 String translatedKeyword = (String )translatedKeywordIter.next(); 681 cs.writeBytes(translatedKeyword); 682 cs.writeByte(0); 683 684 String text = (String )textIter.next(); 685 if (flag == 1) { 686 cs.write(deflate(text)); 687 } else { 688 cs.writeUTF(text); 689 } 690 cs.finish(); 691 } 692 } 693 694 private void write_zTXt() throws IOException { 695 Iterator keywordIter = metadata.zTXt_keyword.iterator(); 696 Iterator methodIter = metadata.zTXt_compressionMethod.iterator(); 697 Iterator textIter = metadata.zTXt_text.iterator(); 698 699 while (keywordIter.hasNext()) { 700 ChunkStream cs = new ChunkStream(PNGImageReader.zTXt_TYPE, stream); 701 String keyword = (String )keywordIter.next(); 702 cs.writeBytes(keyword); 703 cs.writeByte(0); 704 705 int compressionMethod = ((Integer )methodIter.next()).intValue(); 706 cs.writeByte(compressionMethod); 707 708 String text = (String )textIter.next(); 709 cs.write(deflate(text)); 710 cs.finish(); 711 } 712 } 713 714 private void writeUnknownChunks() throws IOException { 715 Iterator typeIter = metadata.unknownChunkType.iterator(); 716 Iterator dataIter = metadata.unknownChunkData.iterator(); 717 718 while (typeIter.hasNext() && dataIter.hasNext()) { 719 String type = (String )typeIter.next(); 720 ChunkStream cs = new ChunkStream(PNGImageReader.chunkType(type), 721 stream); 722 byte[] data = (byte[])dataIter.next(); 723 cs.write(data); 724 cs.finish(); 725 } 726 } 727 728 private void encodePass(ImageOutputStream os, 729 RenderedImage image, 730 int xOffset, int yOffset, 731 int xSkip, int ySkip) throws IOException { 732 int minX = sourceXOffset; 733 int minY = sourceYOffset; 734 int width = sourceWidth; 735 int height = sourceHeight; 736 737 xOffset *= periodX; 739 xSkip *= periodX; 740 yOffset *= periodY; 741 ySkip *= periodY; 742 743 int hpixels = (width - xOffset + xSkip - 1)/xSkip; 745 int vpixels = (height - yOffset + ySkip - 1)/ySkip; 746 if (hpixels == 0 || vpixels == 0) { 747 return; 748 } 749 750 xOffset *= numBands; 752 xSkip *= numBands; 753 754 int samplesPerByte = 8/metadata.IHDR_bitDepth; 756 int numSamples = width*numBands; 757 int[] samples = new int[numSamples]; 758 759 int bytesPerRow = hpixels*numBands; 760 if (metadata.IHDR_bitDepth < 8) { 761 bytesPerRow = (bytesPerRow + samplesPerByte - 1)/samplesPerByte; 762 } else if (metadata.IHDR_bitDepth == 16) { 763 bytesPerRow *= 2; 764 } 765 766 IndexColorModel icm_gray_alpha = null; 767 if (metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA && 768 image.getColorModel() instanceof IndexColorModel ) 769 { 770 bytesPerRow *= 2; 772 773 icm_gray_alpha = (IndexColorModel )image.getColorModel(); 775 } 776 777 currRow = new byte[bytesPerRow + bpp]; 778 prevRow = new byte[bytesPerRow + bpp]; 779 filteredRows = new byte[5][bytesPerRow + bpp]; 780 781 int bitDepth = metadata.IHDR_bitDepth; 782 for (int row = minY + yOffset; row < minY + height; row += ySkip) { 783 Rectangle rect = new Rectangle (minX, row, width, 1); 784 Raster ras = image.getData(rect); 785 if (sourceBands != null) { 786 ras = ras.createChild(minX, row, width, 1, minX, row, 787 sourceBands); 788 } 789 790 ras.getPixels(minX, row, width, 1, samples); 791 792 if (image.getColorModel().isAlphaPremultiplied()) { 793 WritableRaster wr = ras.createCompatibleWritableRaster(); 794 wr.setPixels(wr.getMinX(), wr.getMinY(), 795 wr.getWidth(), wr.getHeight(), 796 samples); 797 798 image.getColorModel().coerceData(wr, false); 799 wr.getPixels(wr.getMinX(), wr.getMinY(), 800 wr.getWidth(), wr.getHeight(), 801 samples); 802 } 803 804 int[] paletteOrder = metadata.PLTE_order; 806 if (paletteOrder != null) { 807 for (int i = 0; i < numSamples; i++) { 808 samples[i] = paletteOrder[samples[i]]; 809 } 810 } 811 812 int count = bpp; int pos = 0; 814 int tmp = 0; 815 816 switch (bitDepth) { 817 case 1: case 2: case 4: 818 820 int mask = samplesPerByte - 1; 821 for (int s = xOffset; s < numSamples; s += xSkip) { 822 byte val = scale0[samples[s]]; 823 tmp = (tmp << bitDepth) | val; 824 825 if ((pos++ & mask) == mask) { 826 currRow[count++] = (byte)tmp; 827 tmp = 0; 828 pos = 0; 829 } 830 } 831 832 if ((pos & mask) != 0) { 834 tmp <<= ((8/bitDepth) - pos)*bitDepth; 835 currRow[count++] = (byte)tmp; 836 } 837 break; 838 839 case 8: 840 if (numBands == 1) { 841 for (int s = xOffset; s < numSamples; s += xSkip) { 842 currRow[count++] = scale0[samples[s]]; 843 if (icm_gray_alpha != null) { 844 currRow[count++] = 845 scale0[icm_gray_alpha.getAlpha(0xff & samples[s])]; 846 } 847 } 848 } else { 849 for (int s = xOffset; s < numSamples; s += xSkip) { 850 for (int b = 0; b < numBands; b++) { 851 currRow[count++] = scale[b][samples[s + b]]; 852 } 853 } 854 } 855 break; 856 857 case 16: 858 for (int s = xOffset; s < numSamples; s += xSkip) { 859 for (int b = 0; b < numBands; b++) { 860 currRow[count++] = scaleh[b][samples[s + b]]; 861 currRow[count++] = scalel[b][samples[s + b]]; 862 } 863 } 864 break; 865 } 866 867 int filterType = rowFilter.filterRow(metadata.IHDR_colorType, 869 currRow, prevRow, 870 filteredRows, 871 bytesPerRow, bpp); 872 873 os.write(filterType); 874 os.write(filteredRows[filterType], bpp, bytesPerRow); 875 876 byte[] swap = currRow; 878 currRow = prevRow; 879 prevRow = swap; 880 881 pixelsDone += hpixels; 882 processImageProgress(100.0F*pixelsDone/totalPixels); 883 884 if (abortRequested()) { 887 return; 888 } 889 } 890 } 891 892 private void write_IDAT(RenderedImage image) throws IOException { 894 IDATOutputStream ios = new IDATOutputStream(stream, 32768); 895 896 if (metadata.IHDR_interlaceMethod == 1) { 897 for (int i = 0; i < 7; i++) { 898 encodePass(ios, image, 899 PNGImageReader.adam7XOffset[i], 900 PNGImageReader.adam7YOffset[i], 901 PNGImageReader.adam7XSubsampling[i], 902 PNGImageReader.adam7YSubsampling[i]); 903 if (abortRequested()) { 904 break; 905 } 906 } 907 } else { 908 encodePass(ios, image, 0, 0, 1, 1); 909 } 910 911 ios.finish(); 912 } 913 914 private void writeIEND() throws IOException { 915 ChunkStream cs = new ChunkStream(PNGImageReader.IEND_TYPE, stream); 916 cs.finish(); 917 } 918 919 private boolean equals(int[] s0, int[] s1) { 922 if (s0 == null || s1 == null) { 923 return false; 924 } 925 if (s0.length != s1.length) { 926 return false; 927 } 928 for (int i = 0; i < s0.length; i++) { 929 if (s0[i] != s1[i]) { 930 return false; 931 } 932 } 933 return true; 934 } 935 936 private void initializeScaleTables(int[] sampleSize) { 940 int bitDepth = metadata.IHDR_bitDepth; 941 942 if (bitDepth == scalingBitDepth && 944 equals(sampleSize, this.sampleSize)) { 945 return; 946 } 947 948 this.sampleSize = sampleSize; 950 this.scalingBitDepth = bitDepth; 951 int maxOutSample = (1 << bitDepth) - 1; 952 if (bitDepth <= 8) { 953 scale = new byte[numBands][]; 954 for (int b = 0; b < numBands; b++) { 955 int maxInSample = (1 << sampleSize[b]) - 1; 956 int halfMaxInSample = maxInSample/2; 957 scale[b] = new byte[maxInSample + 1]; 958 for (int s = 0; s <= maxInSample; s++) { 959 scale[b][s] = 960 (byte)((s*maxOutSample + halfMaxInSample)/maxInSample); 961 } 962 } 963 scale0 = scale[0]; 964 scaleh = scalel = null; 965 } else { scaleh = new byte[numBands][]; 968 scalel = new byte[numBands][]; 969 970 for (int b = 0; b < numBands; b++) { 971 int maxInSample = (1 << sampleSize[b]) - 1; 972 int halfMaxInSample = maxInSample/2; 973 scaleh[b] = new byte[maxInSample + 1]; 974 scalel[b] = new byte[maxInSample + 1]; 975 for (int s = 0; s <= maxInSample; s++) { 976 int val = (s*maxOutSample + halfMaxInSample)/maxInSample; 977 scaleh[b][s] = (byte)(val >> 8); 978 scalel[b][s] = (byte)(val & 0xff); 979 } 980 } 981 scale = null; 982 scale0 = null; 983 } 984 } 985 986 public void write(IIOMetadata streamMetadata, 987 IIOImage image, 988 ImageWriteParam param) throws IIOException { 989 if (stream == null) { 990 throw new IllegalStateException ("output == null!"); 991 } 992 if (image == null) { 993 throw new IllegalArgumentException ("image == null!"); 994 } 995 if (image.hasRaster()) { 996 throw new UnsupportedOperationException ("image has a Raster!"); 997 } 998 999 RenderedImage im = image.getRenderedImage(); 1000 SampleModel sampleModel = im.getSampleModel(); 1001 this.numBands = sampleModel.getNumBands(); 1002 1003 this.sourceXOffset = im.getMinX(); 1005 this.sourceYOffset = im.getMinY(); 1006 this.sourceWidth = im.getWidth(); 1007 this.sourceHeight = im.getHeight(); 1008 this.sourceBands = null; 1009 this.periodX = 1; 1010 this.periodY = 1; 1011 1012 if (param != null) { 1013 Rectangle sourceRegion = param.getSourceRegion(); 1015 if (sourceRegion != null) { 1016 Rectangle imageBounds = new Rectangle (im.getMinX(), 1017 im.getMinY(), 1018 im.getWidth(), 1019 im.getHeight()); 1020 sourceRegion = sourceRegion.intersection(imageBounds); 1022 sourceXOffset = sourceRegion.x; 1023 sourceYOffset = sourceRegion.y; 1024 sourceWidth = sourceRegion.width; 1025 sourceHeight = sourceRegion.height; 1026 } 1027 1028 int gridX = param.getSubsamplingXOffset(); 1030 int gridY = param.getSubsamplingYOffset(); 1031 sourceXOffset += gridX; 1032 sourceYOffset += gridY; 1033 sourceWidth -= gridX; 1034 sourceHeight -= gridY; 1035 1036 periodX = param.getSourceXSubsampling(); 1038 periodY = param.getSourceYSubsampling(); 1039 1040 int[] sBands = param.getSourceBands(); 1041 if (sBands != null) { 1042 sourceBands = sBands; 1043 numBands = sourceBands.length; 1044 } 1045 } 1046 1047 int destWidth = (sourceWidth + periodX - 1)/periodX; 1049 int destHeight = (sourceHeight + periodY - 1)/periodY; 1050 if (destWidth <= 0 || destHeight <= 0) { 1051 throw new IllegalArgumentException ("Empty source region!"); 1052 } 1053 1054 this.totalPixels = destWidth*destHeight; 1056 this.pixelsDone = 0; 1057 1058 IIOMetadata imd = image.getMetadata(); 1060 if (imd != null) { 1061 metadata = (PNGMetadata)convertImageMetadata(imd, 1062 ImageTypeSpecifier.createFromRenderedImage(im), 1063 null); 1064 } else { 1065 metadata = new PNGMetadata(); 1066 } 1067 1068 if (param != null) { 1069 switch (param.getProgressiveMode()) { 1071 case ImageWriteParam.MODE_DEFAULT: 1072 metadata.IHDR_interlaceMethod = 1; 1073 break; 1074 case ImageWriteParam.MODE_DISABLED: 1075 metadata.IHDR_interlaceMethod = 0; 1076 break; 1077 } 1080 } 1081 1082 metadata.initialize(new ImageTypeSpecifier (im), numBands); 1084 1085 metadata.IHDR_width = destWidth; 1087 metadata.IHDR_height = destHeight; 1088 1089 this.bpp = numBands*((metadata.IHDR_bitDepth == 16) ? 2 : 1); 1090 1091 initializeScaleTables(sampleModel.getSampleSize()); 1093 1094 clearAbortRequest(); 1095 1096 processImageStarted(0); 1097 1098 try { 1099 write_magic(); 1100 write_IHDR(); 1101 1102 write_cHRM(); 1103 write_gAMA(); 1104 write_iCCP(); 1105 write_sBIT(); 1106 write_sRGB(); 1107 1108 write_PLTE(); 1109 1110 write_hIST(); 1111 write_tRNS(); 1112 write_bKGD(); 1113 1114 write_pHYs(); 1115 write_sPLT(); 1116 write_tIME(); 1117 write_tEXt(); 1118 write_iTXt(); 1119 write_zTXt(); 1120 1121 writeUnknownChunks(); 1122 1123 write_IDAT(im); 1124 1125 if (abortRequested()) { 1126 processWriteAborted(); 1127 } else { 1128 writeIEND(); 1130 processImageComplete(); 1131 } 1132 } catch (IOException e) { 1133 throw new IIOException ("I/O error writing PNG file!", e); 1134 } 1135 } 1136} 1137 | Popular Tags |