| 1 50 51 package org.openlaszlo.iv.flash.util; 52 53 import java.io.EOFException ; 54 import java.io.IOException ; 55 import java.io.InputStream ; 56 import java.io.DataInputStream ; 57 import java.io.BufferedInputStream ; 58 import java.io.ByteArrayInputStream ; 59 import java.util.zip.InflaterInputStream ; 60 import java.util.zip.Deflater ; 61 import java.util.zip.CRC32 ; 62 import java.util.Hashtable ; 63 import org.openlaszlo.iv.flash.parser.DataMarker; 64 65 70 public class PNGHelper 71 { 72 73 private static final int CHUNK_IHDR = 0x49484452; 74 75 private static final int CHUNK_PLTE = 0x504c5445; 76 77 private static final int CHUNK_IDAT = 0x49444154; 78 79 private static final int CHUNK_IEND = 0x49454e44; 80 81 private static final int CHUNK_tRNS = 0x74524e53; 82 83 84 private static final int CHUNK_bKGD = 0x624b4744; 85 86 private static final int CHUNK_cHRM = 0x6348524d; 87 88 private static final int CHUNK_fRAc = 0x66524163; 89 90 private static final int CHUNK_gAMA = 0x67414d41; 91 92 private static final int CHUNK_gIFg = 0x67494667; 93 94 private static final int CHUNK_gIFt = 0x67494674; 95 96 private static final int CHUNK_gIFx = 0x67494678; 97 98 private static final int CHUNK_hIST = 0x68495354; 99 100 private static final int CHUNK_iCCP = 0x69434350; 101 102 private static final int CHUNK_iTXt = 0x69545874; 103 104 private static final int CHUNK_oFFs = 0x6f464673; 105 106 private static final int CHUNK_pCAL = 0x7043414c; 107 108 private static final int CHUNK_pHYs = 0x70485973; 109 110 private static final int CHUNK_sBIT = 0x73424954; 111 112 private static final int CHUNK_sCAL = 0x7343414c; 113 114 private static final int CHUNK_sPLT = 0x73504c54; 115 116 private static final int CHUNK_sRGB = 0x73524742; 117 118 private static final int CHUNK_tEXt = 0x74455874; 119 120 private static final int CHUNK_tIME = 0x74494d45; 121 122 private static final int CHUNK_zTXt = 0x7a545874; 123 124 125 private static final int[] PNG_SIGN = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; 126 127 128 private boolean flagIHDR = false; 129 130 private boolean flagIDAT = false; 131 132 private boolean flagPLTE = false; 133 134 135 private boolean tRNSPassRequired = false; 136 137 private RGB tRNSKeyColor = null; 138 139 private boolean flagtRNS = false; 140 141 142 private int colorType_components= -1; 143 144 145 private int compressionMethod = -1; 146 153 private int filterMethod = -1; 154 158 private int interlaceMethod = -1; 159 160 161 private int width = -1; 162 163 private int height = -1; 164 165 private int bitDepth = -1; 166 167 168 private long gamma = 45455; 169 170 177 private int colorType = -1; 178 179 180 private boolean colorType_hasPalette= false; 181 182 private boolean colorType_hasAlpha = false; 183 184 private boolean colorType_hasColor = false; 185 186 187 private RGB[] palette; 188 189 190 private int colorsUsed = 0; 191 192 193 private byte[] buffer; 194 195 196 198 199 private CRCInputStream source; 200 201 207 public class CRCInputStream extends DataInputStream  209 { 210 211 private CRC32 crc = new CRC32 (); 212 213 217 public CRCInputStream(InputStream inputStream) 218 { 219 super(inputStream); 220 } 221 222 226 public long getCRC() 227 { 228 return crc.getValue(); 229 } 230 231 234 public void reset() 235 { 236 crc.reset(); 237 } 238 239 243 public int read() throws IOException  244 { 245 int x = super.read(); 246 crc.update(x); 247 return x; 248 } 249 250 254 public int read2() throws IOException , EOFException  255 { 256 int a = read(); 257 int b = read(); 258 return a | b << 8; 259 } 260 261 265 public int read2n() throws IOException , EOFException  266 { 267 int b = read(); 268 int a = read(); 269 return a | b << 8; 270 } 271 272 276 public long read4n() throws IOException , EOFException  277 { 278 long b = (long)read2n(); 279 long a = (long)read2n(); 280 return a | b << 16; 281 } 282 } 283 284 285 290 public class RGB 292 { 293 294 private static final int FORMAT_RGB5 = 0x8050; 295 296 private static final int FORMAT_RGB8 = 0x8051; 297 298 private static final int FORMAT_RGB5_A1 = 0x8057; 299 300 private static final int FORMAT_RGBA8 = 0x8058; 301 302 303 private static final int NO_ALPHA = -1; 304 305 306 private static final float DEFAULT_ALPHA = 1.0f; 307 308 private static final float DEFAULT_FORMAT = 255.0f; 309 310 311 private float red; 312 313 private float green; 314 315 private float blue; 316 317 private float alpha; 318 319 323 public RGB(float gray) 324 { 325 set(gray, gray, gray, DEFAULT_ALPHA); 326 } 327 328 332 public RGB(int gray) 333 { 334 set(gray, gray, gray, NO_ALPHA, NO_ALPHA); 335 } 336 337 342 public RGB(int gray, int a) 343 { 344 set(gray, gray, gray, a, DEFAULT_FORMAT); 345 } 346 347 352 public RGB(float gray, float a) 353 { 354 set(gray, gray, gray, a); 355 } 356 357 363 public RGB(float r, float g, float b) 364 { 365 set(r, g, b, DEFAULT_ALPHA); 366 } 367 368 374 public RGB(int r, int g, int b) 375 { 376 set(r, g, b, NO_ALPHA, NO_ALPHA); 377 } 378 379 386 public RGB(int r, int g, int b, int a) 387 { 388 set((float)r / DEFAULT_FORMAT, (float)g / DEFAULT_FORMAT, (float)b / DEFAULT_FORMAT, (float) a/ DEFAULT_FORMAT); 389 } 390 391 398 public RGB(float r, float g, float b, float a) 399 { 400 set(r, g, b, a); 401 } 402 403 410 public void set(float r, float g, float b, float a) 411 { 412 red = r > 1.0f ? 1.0f : (r < 0.0f ? 0.0f : r); 413 green = g > 1.0f ? 1.0f : (g < 0.0f ? 0.0f : g); 414 blue = b > 1.0f ? 1.0f : (b < 0.0f ? 0.0f : b); 415 alpha = a > 1.0f ? 1.0f : (a < 0.0f ? 0.0f : a); 416 } 417 418 426 public void set(int r, int g, int b, int a, float format) 427 { 428 set((float)r / DEFAULT_FORMAT, (float)g / DEFAULT_FORMAT, (float)b / DEFAULT_FORMAT, 429 format == NO_ALPHA ? DEFAULT_ALPHA : (float)a / format); 430 } 431 432 437 public int[] getPacked(int format) 438 { 439 int r; 440 int g; 441 int b; 442 switch(format) 443 { 444 case FORMAT_RGB5: 445 r = (int)(red * 32.0f); 446 g = (int)(green * 32.0f); 447 b = (int)(blue * 32.0f); 448 return new int[] 449 { 450 r + (g & 0x07) << 5, 451 (g & 0x18) >> 3 + (b & 0x1f) << 2 452 }; 453 case FORMAT_RGB8: 454 return new int[] 455 { 456 (int)(red * 255.0f), 457 (int)(green * 255.0f), 458 (int)(blue * 255.0f) 459 }; 460 case FORMAT_RGB5_A1: 461 r = (int)(red * 32.0f); 462 g = (int)(green * 32.0f); 463 b = (int)(blue * 32.0f); 464 return new int[] 465 { 466 r + (g & 0x07) << 5, 467 (g & 0x18) >> 3 + (b & 0x1f) << 2 + (alpha > 0.5f ? 128 : 0) 468 }; 469 case FORMAT_RGBA8: 470 return new int[] 471 { 472 (int)(red * 255.0f), 473 (int)(green * 255.0f), 474 (int)(blue * 255.0f), 475 (int)(alpha * 255.0f) 476 }; 477 default: 478 throw new IllegalArgumentException ("Unknown color format "+format); 479 } 480 } 481 482 486 public float getRed() 487 { 488 return red; 489 } 490 491 495 public float getGreen() 496 { 497 return green; 498 } 499 500 504 public float getBlue() 505 { 506 return blue; 507 } 508 509 513 public float getAlpha() 514 { 515 return alpha; 516 } 517 518 522 public void setAlpha(float alpha) 523 { 524 this.alpha = alpha; 525 } 526 527 533 public boolean compareTo(RGB rgb, boolean compareAlpha) 534 { 535 if (rgb.getRed() == red && rgb.getGreen() == green && rgb.getBlue() == blue) 536 if (!compareAlpha || (compareAlpha && rgb.getAlpha() == alpha)) 537 return true; 538 return false; 539 } 540 } 541 542 547 public PNGHelper() 548 { } 549 550 554 public PNGHelper(byte[] inputBuffer) 555 { 556 setInputBuffer(inputBuffer); 557 } 558 559 564 public void setInputBuffer(byte[] inputBuffer) 565 { 566 source = new CRCInputStream(new ByteArrayInputStream (inputBuffer)); 567 } 568 569 574 public void setInputBuffer(FlashBuffer fob) 575 { 576 source = new CRCInputStream(fob.getInputStream()); 578 } 579 580 584 public int getWidth() 585 { 586 return width; 587 } 588 589 594 public int getHeight() 595 { 596 return height; 597 } 598 599 603 public int getColorTableSize() 604 { 605 return (colorsUsed -1); 606 } 607 608 614 public boolean hasTransparency() 615 { 616 return (colorType>3 || flagtRNS); 617 } 618 619 626 public int getFormat() 627 { 628 int format; 629 switch (colorType_components) 630 { 631 case 1: 632 format = 3; if (colorType == 0) 634 format = 5; 635 if (bitDepth == 16) 636 format = 5; 637 break; 638 default: 642 format = 5; break; 644 } 645 return format; 646 } 647 648 659 public DataMarker getZlibData() throws IOException ,EOFException ,IVException 660 { 661 RGB[][] rgb = read(); 662 boolean transparency = hasTransparency(); 663 int format = getFormat(); 664 int mult = 1; 665 switch (format) 666 { 667 case 4: 668 mult = 2; 669 break; 670 case 5: 671 mult = 4; 672 } 673 byte[] tempData; 674 int plus = 3; 675 int[] pixel; 676 int idx = 0; 677 int falsewidth = width; 678 int added = 0; 679 int maxpixels = width * height; 680 if ((mult == 1) && (width % 4 > 0)) 682 { 683 while (falsewidth % 4 > 0) 684 { 685 falsewidth++; 686 added++; 687 } 688 maxpixels = falsewidth * height; 689 } 690 691 if (colorType_hasPalette) 692 { 693 if (transparency) plus++; 695 696 tempData = new byte[(maxpixels) + (colorsUsed*plus)]; for (int i = 0; i < colorsUsed; i++) 698 { 699 if (transparency) 700 pixel = palette[i].getPacked(RGB.FORMAT_RGBA8); 701 else 702 pixel = palette[i].getPacked(RGB.FORMAT_RGB8); 703 704 if (transparency && pixel[3] == 0x00) { 706 tempData[idx++] = 0; 707 tempData[idx++] = 0; 708 tempData[idx++] = 0; 709 } 710 else 711 { 712 tempData[idx++] = (byte) ((pixel[0])); 713 tempData[idx++] = (byte) ((pixel[1])); 714 tempData[idx++] = (byte) ((pixel[2])); 715 } 716 if (transparency) 717 tempData[idx++] = (byte) ((pixel[3])); 718 } 719 } 720 else 721 { 722 if (transparency) 723 mult = 4; 724 tempData = new byte[(maxpixels * mult)]; } 726 727 for (int y = 0; y < height; y++) 728 { 729 for (int x = 0; x < width; x++) 730 { 731 if (colorType_hasPalette) 732 { 733 if (transparency) 734 pixel = rgb[y][x].getPacked(RGB.FORMAT_RGBA8); 735 else 736 pixel = rgb[y][x].getPacked(RGB.FORMAT_RGB8); 737 int b = 0; 738 for (int i = 0; i < palette.length; i++) 739 { 740 if (palette[i].compareTo(rgb[y][x],false)) 741 { 742 b = i; 743 break; 744 } 745 } 746 tempData[idx++] = (byte) b; 747 } 748 else 749 { 750 if (mult == 4) 751 pixel = rgb[y][x].getPacked(RGB.FORMAT_RGBA8); 752 else if (mult == 2) 753 pixel = rgb[y][x].getPacked(RGB.FORMAT_RGB5_A1); 754 else 755 pixel = rgb[y][x].getPacked(RGB.FORMAT_RGB8); 756 757 if (pixel != null) 758 { 759 if (mult == 4) 760 tempData[idx++] = (byte) ((pixel[3])); 761 tempData[idx++] = (byte) ((pixel[0])); 762 if (mult > 1) 763 tempData[idx++] = (byte) ((pixel[1])); 764 if (mult == 4) 765 tempData[idx++] = (byte) ((pixel[2])); 766 } 767 } 768 } 769 if (added > 0) { 771 for (int i = 0; i < added; i++) 772 { 773 tempData[idx++] = 0x00; 774 } 775 } 776 } 777 Deflater deflater = new Deflater (9); deflater.setInput(tempData); 779 byte[] data = new byte[(maxpixels * mult) + (colorsUsed*plus)]; 780 deflater.finish(); 781 int defsize = deflater.deflate(data); 782 byte[] buff = new byte[defsize]; 783 System.arraycopy(data,0,buff,0,defsize); 784 DataMarker zlibDatas = new DataMarker(buff,0,defsize); 785 return zlibDatas; 786 } 787 788 791 792 798 private RGB[][] read() throws IOException ,EOFException ,IVException 799 { 800 if (source == null) 801 throw new IOException ("Null input stream"); 802 source.mark(Integer.MAX_VALUE); 803 buffer = new byte[0]; 804 try 805 { 806 for (int i = 0; i < 8; i++) 807 if (source.read() != PNG_SIGN[i]) 808 throw new IOException ("Not a valid PNG file"); 809 810 int length; 811 int id; 812 while(true) 813 { 814 &n
|