1
89
90 package com.lowagie.text.pdf.codec;
91
92 import java.awt.color.ICC_Profile;
93 import java.io.ByteArrayInputStream;
94 import java.io.ByteArrayOutputStream;
95 import java.io.DataInputStream;
96 import java.io.IOException;
97 import java.io.InputStream;
98 import java.net.URL;
99 import java.util.zip.Inflater;
100 import java.util.zip.InflaterInputStream;
101
102 import com.lowagie.text.ExceptionConverter;
103 import com.lowagie.text.Image;
104 import com.lowagie.text.ImgRaw;
105 import com.lowagie.text.Utilities;
106 import com.lowagie.text.pdf.ByteBuffer;
107 import com.lowagie.text.pdf.PdfArray;
108 import com.lowagie.text.pdf.PdfDictionary;
109 import com.lowagie.text.pdf.PdfLiteral;
110 import com.lowagie.text.pdf.PdfName;
111 import com.lowagie.text.pdf.PdfNumber;
112 import com.lowagie.text.pdf.PdfObject;
113 import com.lowagie.text.pdf.PdfReader;
114 import com.lowagie.text.pdf.PdfString;
115
116
122 public class PngImage {
123
124 public static final int[] PNGID = {137, 80, 78, 71, 13, 10, 26, 10};
125
126
127 public static final String IHDR = "IHDR";
128
129
130 public static final String PLTE = "PLTE";
131
132
133 public static final String IDAT = "IDAT";
134
135
136 public static final String IEND = "IEND";
137
138
139 public static final String tRNS = "tRNS";
140
141
142 public static final String pHYs = "pHYs";
143
144
145 public static final String gAMA = "gAMA";
146
147
148 public static final String cHRM = "cHRM";
149
150
151 public static final String sRGB = "sRGB";
152
153
154 public static final String iCCP = "iCCP";
155
156 private static final int TRANSFERSIZE = 4096;
157 private static final int PNG_FILTER_NONE = 0;
158 private static final int PNG_FILTER_SUB = 1;
159 private static final int PNG_FILTER_UP = 2;
160 private static final int PNG_FILTER_AVERAGE = 3;
161 private static final int PNG_FILTER_PAETH = 4;
162 private static final PdfName intents[] = {PdfName.PERCEPTUAL,
163 PdfName.RELATIVECOLORIMETRIC,PdfName.SATURATION,PdfName.ABSOLUTECOLORIMETRIC};
164
165 InputStream is;
166 DataInputStream dataStream;
167 int width;
168 int height;
169 int bitDepth;
170 int colorType;
171 int compressionMethod;
172 int filterMethod;
173 int interlaceMethod;
174 PdfDictionary additional = new PdfDictionary();
175 byte image[];
176 byte smask[];
177 byte trans[];
178 NewByteArrayOutputStream idat = new NewByteArrayOutputStream();
179 int dpiX;
180 int dpiY;
181 float XYRatio;
182 boolean genBWMask;
183 boolean palShades;
184 int transRedGray = -1;
185 int transGreen = -1;
186 int transBlue = -1;
187 int inputBands;
188 int bytesPerPixel;
189 byte colorTable[];
190 float gamma = 1f;
191 boolean hasCHRM = false;
192 float xW, yW, xR, yR, xG, yG, xB, yB;
193 PdfName intent;
194 ICC_Profile icc_profile;
195
196
197
198
199 PngImage(InputStream is) {
200 this.is = is;
201 }
202
203
208 public static Image getImage(URL url) throws IOException {
209 InputStream is = null;
210 try {
211 is = url.openStream();
212 Image img = getImage(is);
213 img.setUrl(url);
214 return img;
215 }
216 finally {
217 if (is != null) {
218 is.close();
219 }
220 }
221 }
222
223
228 public static Image getImage(InputStream is) throws IOException {
229 PngImage png = new PngImage(is);
230 return png.getImage();
231 }
232
233
238 public static Image getImage(String file) throws IOException {
239 return getImage(Utilities.toURL(file));
240 }
241
242
247 public static Image getImage(byte data[]) throws IOException {
248 ByteArrayInputStream is = new ByteArrayInputStream(data);
249 Image img = getImage(is);
250 img.setOriginalData(data);
251 return img;
252 }
253
254 boolean checkMarker(String s) {
255 if (s.length() != 4)
256 return false;
257 for (int k = 0; k < 4; ++k) {
258 char c = s.charAt(k);
259 if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z'))
260 return false;
261 }
262 return true;
263 }
264
265 void readPng() throws IOException {
266 for (int i = 0; i < PNGID.length; i++) {
267 if (PNGID[i] != is.read()) {
268 throw new IOException("File is not a valid PNG.");
269 }
270 }
271 byte buffer[] = new byte[TRANSFERSIZE];
272 while (true) {
273 int len = getInt(is);
274 String marker = getString(is);
275 if (len < 0 || !checkMarker(marker))
276 throw new IOException("Corrupted PNG file.");
277 if (IDAT.equals(marker)) {
278 int size;
279 while (len != 0) {
280 size = is.read(buffer, 0, Math.min(len, TRANSFERSIZE));
281 if (size < 0)
282 return;
283 idat.write(buffer, 0, size);
284 len -= size;
285 }
286 }
287 else if (tRNS.equals(marker)) {
288 switch (colorType) {
289 case 0:
290 if (len >= 2) {
291 len -= 2;
292 int gray = getWord(is);
293 if (bitDepth == 16)
294 transRedGray = gray;
295 else
296 additional.put(PdfName.MASK, new PdfLiteral("["+gray+" "+gray+"]"));
297 }
298 break;
299 case 2:
300 if (len >= 6) {
301 len -= 6;
302 int red = getWord(is);
303 int green = getWord(is);
304 int blue = getWord(is);
305 if (bitDepth == 16) {
306 transRedGray = red;
307 transGreen = green;
308 transBlue = blue;
309 }
310 else
311 additional.put(PdfName.MASK, new PdfLiteral("["+red+" "+red+" "+green+" "+green+" "+blue+" "+blue+"]"));
312 }
313 break;
314 case 3:
315 if (len > 0) {
316 trans = new byte[len];
317 for (int k = 0; k < len; ++k)
318 trans[k] = (byte)is.read();
319 len = 0;
320 }
321 break;
322 }
323 Utilities.skip(is, len);
324 }
325 else if (IHDR.equals(marker)) {
326 width = getInt(is);
327 height = getInt(is);
328
329 bitDepth = is.read();
330 colorType = is.read();
331 compressionMethod = is.read();
332 filterMethod = is.read();
333 interlaceMethod = is.read();
334 }
335 else if (PLTE.equals(marker)) {
336 if (colorType == 3) {
337 PdfArray colorspace = new PdfArray();
338 colorspace.add(PdfName.INDEXED);
339 colorspace.add(getColorspace());
340 colorspace.add(new PdfNumber(len / 3 - 1));
341 ByteBuffer colortable = new ByteBuffer();
342 while ((len--) > 0) {
343 colortable.append_i(is.read());
344 }
345 colorspace.add(new PdfString(colorTable = colortable.toByteArray()));
346 additional.put(PdfName.COLORSPACE, colorspace);
347 }
348 else {
349 Utilities.skip(is, len);
350 }
351 }
352 else if (pHYs.equals(marker)) {
353 int dx = getInt(is);
354 int dy = getInt(is);
355 int unit = is.read();
356 if (unit == 1) {
357 dpiX = (int)(dx * 0.0254f + 0.5f);
358 dpiY = (int)(dy * 0.0254f + 0.5f);
359 }
360 else {
361 if (dy != 0)
362 XYRatio = (float)dx / (float)dy;
363 }
364 }
365 else if (cHRM.equals(marker)) {
366 xW = getInt(is) / 100000f;
367 yW = getInt(is) / 100000f;
368 xR = getInt(is) / 100000f;
369 yR = getInt(is) / 100000f;
370 xG = getInt(is) / 100000f;
371 yG = getInt(is) / 100000f;
372 xB = getInt(is) / 100000f;
373 yB = getInt(is) / 100000f;
374 hasCHRM = !(Math.abs(xW)<0.0001f||Math.abs(yW)<0.0001f||Math.abs(xR)<0.0001f||Math.abs(yR)<0.0001f||Math.abs(xG)<0.0001f||Math.abs(yG)<0.0001f||Math.abs(xB)<0.0001f||Math.abs(yB)<0.0001f);
375 }
376 else if (sRGB.equals(marker)) {
377 int ri = is.read();
378 intent = intents[ri];
379 gamma = 2.2f;
380 xW = 0.3127f;
381 yW = 0.329f;
382 xR = 0.64f;
383 yR = 0.33f;
384 xG = 0.3f;
385 yG = 0.6f;
386 xB = 0.15f;
387 yB = 0.06f;
388 hasCHRM = true;
389 }
390 else if (gAMA.equals(marker)) {
391 int gm = getInt(is);
392 if (gm != 0) {
393 gamma = 100000f / gm;
394 if (!hasCHRM) {
395 xW = 0.3127f;
396 yW = 0.329f;
397 xR = 0.64f;
398 yR = 0.33f;
399 xG = 0.3f;
400 yG = 0.6f;
401 xB = 0.15f;
402 yB = 0.06f;
403 hasCHRM = true;
404 }
405 }
406 }
407 else if (iCCP.equals(marker)) {
408 do {
409 --len;
410 } while (is.read() != 0);
411 is.read();
412 --len;
413 byte icccom[] = new byte[len];
414 int p = 0;
415 while (len > 0) {
416 int r = is.read(icccom, p, len);
417 if (r < 0)
418 throw new IOException("Premature end of file.");
419 p += r;
420 len -= r;
421 }
422 byte iccp[] = PdfReader.FlateDecode(icccom, true);
423 icccom = null;
424 try {
425 icc_profile = ICC_Profile.getInstance(iccp);
426 }
427 catch (RuntimeException e) {
428 icc_profile = null;
429 }
430 }
431 else if (IEND.equals(marker)) {
432 break;
433 }
434 else {
435 Utilities.skip(is, len);
436 }
437 Utilities.skip(is, 4);
438 }
439 }
440
441 PdfObject getColorspace() {
442 if (icc_profile != null) {
443 if ((colorType & 2) == 0)
444 return PdfName.DEVICEGRAY;
445 else
446 return PdfName.DEVICERGB;
447 }
448 if (gamma == 1f && !hasCHRM) {
449 if ((colorType & 2) == 0)
450 return PdfName.DEVICEGRAY;
451 else
452 return PdfName.DEVICERGB;
453 }
454 else {
455 PdfArray array = new PdfArray();
456 PdfDictionary dic = new PdfDictionary();
457 if ((colorType & 2) == 0) {
458 if (gamma == 1f)
459 return PdfName.DEVICEGRAY;
460 array.add(PdfName.CALGRAY);
461 dic.put(PdfName.GAMMA, new PdfNumber(gamma));
462 dic.put(PdfName.WHITEPOINT, new PdfLiteral("[1 1 1]"));
463 array.add(dic);
464 }
465 else {
466 PdfObject wp = new PdfLiteral("[1 1 1]");
467 array.add(PdfName.CALRGB);
468 if (gamma != 1f) {
469 PdfArray gm = new PdfArray();
470 PdfNumber n = new PdfNumber(gamma);
471 gm.add(n);
472 gm.add(n);
473 gm.add(n);
474 dic.put(PdfName.GAMMA, gm);
475 }
476 if (hasCHRM) {
477 float z = yW*((xG-xB)*yR-(xR-xB)*yG+(xR-xG)*yB);
478 float YA = yR*((xG-xB)*yW-(xW-xB)*yG+(xW-xG)*yB)/z;
479 float XA = YA*xR/yR;
480 float ZA = YA*((1-xR)/yR-1);
481 float YB = -yG*((xR-xB)*yW-(xW-xB)*yR+(xW-xR)*yB)/z;
482 float XB = YB*xG/yG;
483 float ZB = YB*((1-xG)/yG-1);
484 float YC = yB*((xR-xG)*yW-(xW-xG)*yW+(xW-xR)*yG)/z;
485 float XC = YC*xB/yB;
486 float ZC = YC*((1-xB)/yB-1);
487 float XW = XA+XB+XC;
488 float YW = 1;
489 float ZW = ZA+ZB+ZC;
490 PdfArray wpa = new PdfArray();
491 wpa.add(new PdfNumber(XW));
492 wpa.add(new PdfNumber(YW));
493 wpa.add(new PdfNumber(ZW));
494 wp = wpa;
495 PdfArray matrix = new PdfArray();
496 matrix.add(new PdfNumber(XA));
497 matrix.add(new PdfNumber(YA));
498 matrix.add(new PdfNumber(ZA));
499 matrix.add(new PdfNumber(XB));
500 matrix.add(new PdfNumber(YB));
501 matrix.add(new PdfNumber(ZB));
502 matrix.add(new PdfNumber(XC));
503 matrix.add(new PdfNumber(YC));
504 matrix.add(new PdfNumber(ZC));
505 dic.put(PdfName.MATRIX, matrix);
506 }
507 dic.put(PdfName.WHITEPOINT, wp);
508 array.add(dic);
509 }
510 return array;
511 }
512 }
513
514 Image getImage() throws IOException {
515 readPng();
516 try {
517 int pal0 = 0;
518 int palIdx = 0;
519 palShades = false;
520 if (trans != null) {
521 for (int k = 0; k < trans.length; ++k) {
522 int n = trans[k] & 0xff;
523 if (n == 0) {
524 ++pal0;
525 palIdx = k;
526 }
527 if (n != 0 && n != 255) {
528 palShades = true;
529 break;
530 }
531 }
532 }
533 if ((colorType & 4) != 0)
534 palShades = true;
535 genBWMask = (!palShades && (pal0 > 1 || transRedGray >= 0));
536 if (!palShades && !genBWMask && pal0 == 1) {
537 additional.put(PdfName.MASK, new PdfLiteral("["+palIdx+" "+palIdx+"]"));
538 }
539 boolean needDecode = (interlaceMethod == 1) || (bitDepth == 16) || ((colorType & 4) != 0) || palShades || genBWMask;
540 switch (colorType) {
541 case 0:
542 inputBands = 1;
543 break;
544 case 2:
545 inputBands = 3;
546 break;
547 case 3:
548 inputBands = 1;
549 break;
550 case 4:
551 inputBands = 2;
552 break;
553 case 6:
554 inputBands = 4;
555 break;
556 }
557 if (needDecode)
558 decodeIdat();
559 int components = inputBands;
560 if ((colorType & 4) != 0)
561 --components;
562 int bpc = bitDepth;
563 if (bpc == 16)
564 bpc = 8;
565 Image img;
566 if (image != null) {
567 if (colorType == 3)
568 img = new ImgRaw(width, height, components, bpc, image);
569 else
570 img = Image.getInstance(width, height, components, bpc, image);
571 }
572 else {
573 img = new ImgRaw(width, height, components, bpc, idat.toByteArray());
574 img.setDeflated(true);
575 PdfDictionary decodeparms = new PdfDictionary();
576 decodeparms.put(PdfName.BITSPERCOMPONENT, new PdfNumber(bitDepth));
577 decodeparms.put(PdfName.PREDICTOR, new PdfNumber(15));
578 decodeparms.put(PdfName.COLUMNS, new PdfNumber(width));
579 decodeparms.put(PdfName.COLORS, new PdfNumber((colorType == 3 || (colorType & 2) == 0) ? 1 : 3));
580 additional.put(PdfName.DECODEPARMS, decodeparms);
581 }
582 if (additional.get(PdfName.COLORSPACE) == null)
583 additional.put(PdfName.COLORSPACE, getColorspace());
584 if (intent != null)
585 additional.put(PdfName.INTENT, intent);
586 if (additional.size() > 0)
587 img.setAdditional(additional);
588 if (icc_profile != null)
589 img.tagICC(icc_profile);
590 if (palShades) {
591 Image im2 = Image.getInstance(width, height, 1, 8, smask);
592 im2.makeMask();
593 img.setImageMask(im2);
594 }
595 if (genBWMask) {
596 Image im2 = Image.getInstance(width, height, 1, 1, smask);
597 im2.makeMask();
598 img.setImageMask(im2);
599 }
600 img.setDpi(dpiX, dpiY);
601 img.setXYRatio(XYRatio);
602 img.setOriginalType(Image.ORIGINAL_PNG);
603 return img;
604 }
605 catch (Exception e) {
606 throw new ExceptionConverter(e);
607 }
608 }
609
610 void decodeIdat() {
611 int nbitDepth = bitDepth;
612 if (nbitDepth == 16)
613 nbitDepth = 8;
614 int size = -1;
615 bytesPerPixel = (bitDepth == 16) ? 2 : 1;
616 switch (colorType) {
617 case 0:
618 size = (nbitDepth * width + 7) / 8 * height;
619 break;
620 case 2:
621 size = width * 3 * height;
622 bytesPerPixel *= 3;
623 break;
624 case 3:
625 if (interlaceMethod == 1)
626 size = (nbitDepth * width + 7) / 8 * height;
627 bytesPerPixel = 1;
628 break;
629 case 4:
630 size = width * height;
631 bytesPerPixel *= 2;
632 break;
633 case 6:
634 size = width * 3 * height;
635 bytesPerPixel *= 4;
636 break;
637 }
638 if (size >= 0)
639 image = new byte[size];
640 if (palShades)
641 smask = new byte[width * height];
642 else if (genBWMask)
643 smask = new byte[(width + 7) / 8 * height];
644 ByteArrayInputStream bai = new ByteArrayInputStream(idat.getBuf(), 0, idat.size());
645 InputStream infStream = new InflaterInputStream(bai, new Inflater());
646 dataStream = new DataInputStream(infStream);
647
648 if (interlaceMethod != 1) {
649 decodePass(0, 0, 1, 1, width, height);
650 }
651 else {
652 decodePass(0, 0, 8, 8, (width + 7)/8, (height + 7)/8);
653 decodePass(4, 0, 8, 8, (width + 3)/8, (height + 7)/8);
654 decodePass(0, 4, 4, 8, (width + 3)/4, (height + 3)/8);
655 decodePass(2, 0, 4, 4, (width + 1)/4, (height + 3)/4);
656 decodePass(0, 2, 2, 4, (width + 1)/2, (height + 1)/4);
657 decodePass(1, 0, 2, 2, width/2, (height + 1)/2);
658 decodePass(0, 1, 1, 2, width, height/2);
659 }
660
661 }
662
663 void decodePass( int xOffset, int yOffset,
664 int xStep, int yStep,
665 int passWidth, int passHeight) {
666 if ((passWidth == 0) || (passHeight == 0)) {
667 return;
668 }
669
670 int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8;
671 byte[] curr = new byte[bytesPerRow];
672 byte[] prior = new byte[bytesPerRow];
673
674
675 int srcY, dstY;
676 for (srcY = 0, dstY = yOffset;
677 srcY < passHeight;
678 srcY++, dstY += yStep) {
679
680 int filter = 0;
681 try {
682 filter = dataStream.read();
683 dataStream.readFully(curr, 0, bytesPerRow);
684 } catch (Exception e) {
685
686 }
687
688 switch (filter) {
689 case PNG_FILTER_NONE:
690 break;
691 case PNG_FILTER_SUB:
692 decodeSubFilter(curr, bytesPerRow, bytesPerPixel);
693 break;
694 case PNG_FILTER_UP:
695 decodeUpFilter(curr, prior, bytesPerRow);
696 break;
697 case PNG_FILTER_AVERAGE:
698 decodeAverageFilter(curr, prior, bytesPerRow, bytesPerPixel);
699 break;
700 case PNG_FILTER_PAETH:
701 decodePaethFilter(curr, prior, bytesPerRow, bytesPerPixel);
702 break;
703 default:
704
705 throw new RuntimeException("PNG filter unknown.");
706 }
707
708 processPixels(curr, xOffset, xStep, dstY, passWidth);
709
710
711 byte[] tmp = prior;
712 prior = curr;
713 curr = tmp;
714 }
715 }
716
717 void processPixels(byte curr[], int xOffset, int step, int y, int width) {
718 int srcX, dstX;
719
720 int out[] = getPixel(curr);
721 int sizes = 0;
722 switch (colorType) {
723 case 0:
724 case 3:
725 case 4:
726 sizes = 1;
727 break;
728 case 2:
729 case 6:
730 sizes = 3;
731 break;
732 }
733 if (image != null) {
734 dstX = xOffset;
735 int yStride = (sizes*this.width*(bitDepth == 16 ? 8 : bitDepth)+ 7)/8;
736 for (srcX = 0; srcX < width; srcX++) {
737 setPixel(image, out, inputBands * srcX, sizes, dstX, y, bitDepth, yStride);
738 dstX += step;
739 }
740 }
741 if (palShades) {
742 if ((colorType & 4) != 0) {
743 if (bitDepth == 16) {
744 for (int k = 0; k < width; ++k)
745 out[k * inputBands + sizes] >>>= 8;
746 }
747 int yStride = this.width;
748 dstX = xOffset;
749 for (srcX = 0; srcX < width; srcX++) {
750 setPixel(smask, out, inputBands * srcX + sizes, 1, dstX, y, 8, yStride);
751 dstX += step;
752 }
753 }
754 else {
755 int yStride = this.width;
756 int v[] = new int[1];
757 dstX = xOffset;
758 for (srcX = 0; srcX < width; srcX++) {
759 int idx = out[srcX];
760 if (idx < trans.length)
761 v[0] = trans[idx];
762 else
763 v[0] = 255;
764 setPixel(smask, v, 0, 1, dstX, y, 8, yStride);
765 dstX += step;
766 }
767 }
768 }
769 else if (genBWMask) {
770 switch (colorType) {
771 case 3: {
772 int yStride = (this.width + 7) / 8;
773 int v[] = new int[1];
774 dstX = xOffset;
775 for (srcX = 0; srcX < width; srcX++) {
776 int idx = out[srcX];
777 v[0] = ((idx < trans.length && trans[idx] == 0) ? 1 : 0);
778 setPixel(smask, v, 0, 1, dstX, y, 1, yStride);
779 dstX += step;
780 }
781 break;
782 }
783 case 0: {
784 int yStride = (this.width + 7) / 8;
785 int v[] = new int[1];
786 dstX = xOffset;
787 for (srcX = 0; srcX < width; srcX++) {
788 int g = out[srcX];
789 v[0] = (g == transRedGray ? 1 : 0);
790 setPixel(smask, v, 0, 1, dstX, y, 1, yStride);
791 dstX += step;
792 }
793 break;
794 }
795 case 2: {
796 int yStride = (this.width + 7) / 8;
797 int v[] = new int[1];
798 dstX = xOffset;
799 for (srcX = 0; srcX < width; srcX++) {
800 int markRed = inputBands * srcX;
801 v[0] = (out[markRed] == transRedGray && out[markRed + 1] == transGreen
802 && out[markRed + 2] == transBlue ? 1 : 0);
803 setPixel(smask, v, 0, 1, dstX, y, 1, yStride);
804 dstX += step;
805 }
806 break;
807 }
808 }
809 }
810 }
811
812 static int getPixel(byte image[], int x, int y, int bitDepth, int bytesPerRow) {
813 if (bitDepth == 8) {
814 int pos = bytesPerRow * y + x;
815 return image[pos] & 0xff;
816 }
817 else {
818 int pos = bytesPerRow * y + x / (8 / bitDepth);
819 int v = image[pos] >> (8 - bitDepth * (x % (8 / bitDepth))- bitDepth);
820 return v & ((1 << bitDepth) - 1);
821 }
822 }
823
824 static void setPixel(byte image[], int data[], int offset, int size, int x, int y, int bitDepth, int bytesPerRow) {
825 if (bitDepth == 8) {
826 int pos = bytesPerRow * y + size * x;
827 for (int k = 0; k < size; ++k)
828 image[pos + k] = (byte)data[k + offset];
829 }
830 else if (bitDepth == 16) {
831 int pos = bytesPerRow * y + size * x;
832 for (int k = 0; k < size; ++k)
833 image[pos + k] = (byte)(data[k + offset] >>> 8);
834 }
835 else {
836 int pos = bytesPerRow * y + x / (8 / bitDepth);
837 int v = data[offset] << (8 - bitDepth * (x % (8 / bitDepth))- bitDepth);
838 image[pos] |= v;
839 }
840 }
841
842 int[] getPixel(byte curr[]) {
843 switch (bitDepth) {
844 case 8: {
845 int out[] = new int[curr.length];
846 for (int k = 0; k < out.length; ++k)
847 out[k] = curr[k] & 0xff;
848 return out;
849 }
850 case 16: {
851 int out[] = new int[curr.length / 2];
852 for (int k = 0; k < out.length; ++k)
853 out[k] = ((curr[k * 2] & 0xff) << 8) + (curr[k * 2 + 1] & 0xff);
854 return out;
855 }
856 default: {
857 int out[] = new int[curr.length * 8 / bitDepth];
858 int idx = 0;
859 int passes = 8 / bitDepth;
860 int mask = (1 << bitDepth) - 1;
861 for (int k = 0; k < curr.length; ++k) {
862 for (int j = passes - 1; j >= 0; --j) {
863 out[idx++] = (curr[k] >>> (bitDepth * j)) & mask;
864 }
865 }
866 return out;
867 }
868 }
869 }
870
871 private static void decodeSubFilter(byte[] curr, int count, int bpp) {
872 for (int i = bpp; i < count; i++) {
873 int val;
874
875 val = curr[i] & 0xff;
876 val += curr[i - bpp] & 0xff;
877
878 curr[i] = (byte)val;
879 }
880 }
881
882 private static void decodeUpFilter(byte[] curr, byte[] prev,
883 int count) {
884 for (int i = 0; i < count; i++) {
885 int raw = curr[i] & 0xff;
886 int prior = prev[i] & 0xff;
887
888 curr[i] = (byte)(raw + prior);
889 }
890 }
891
892 private static void decodeAverageFilter(byte[] curr, byte[] prev,
893 int count, int bpp) {
894 int raw, priorPixel, priorRow;
895
896 for (int i = 0; i < bpp; i++) {
897 raw = curr[i] & 0xff;
898 priorRow = prev[i] & 0xff;
899
900 curr[i] = (byte)(raw + priorRow/2);
901 }
902
903 for (int i = bpp; i < count; i++) {
904 raw = curr[i] & 0xff;
905 priorPixel = curr[i - bpp] & 0xff;
906 priorRow = prev[i] & 0xff;
907
908 curr[i] = (byte)(raw + (priorPixel + priorRow)/2);
909 }
910 }
911
912 private static int paethPredictor(int a, int b, int c) {
913 int p = a + b - c;
914 int pa = Math.abs(p - a);
915 int pb = Math.abs(p - b);
916 int pc = Math.abs(p - c);
917
918 if ((pa <= pb) && (pa <= pc)) {
919 return a;
920 } else if (pb <= pc) {
921 return b;
922 } else {
923 return c;
924 }
925 }
926
927 private static void decodePaethFilter(byte[] curr, byte[] prev,
928 int count, int bpp) {
929 int raw, priorPixel, priorRow, priorRowPixel;
930
931 for (int i = 0; i < bpp; i++) {
932 raw = curr[i] & 0xff;
933 priorRow = prev[i] & 0xff;
934
935 curr[i] = (byte)(raw + priorRow);
936 }
937
938 for (int i = bpp; i < count; i++) {
939 raw = curr[i] & 0xff;
940 priorPixel = curr[i - bpp] & 0xff;
941 priorRow = prev[i] & 0xff;
942 priorRowPixel = prev[i - bpp] & 0xff;
943
944 curr[i] = (byte)(raw + paethPredictor(priorPixel,
945 priorRow,
946 priorRowPixel));
947 }
948 }
949
950 static class NewByteArrayOutputStream extends ByteArrayOutputStream {
951 public byte[] getBuf() {
952 return buf;
953 }
954 }
955
956
962
963 public static final int getInt(InputStream is) throws IOException {
964 return (is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read();
965 }
966
967
973
974 public static final int getWord(InputStream is) throws IOException {
975 return (is.read() << 8) + is.read();
976 }
977
978
984
985 public static final String getString(InputStream is) throws IOException {
986 StringBuffer buf = new StringBuffer();
987 for (int i = 0; i < 4; i++) {
988 buf.append((char)is.read());
989 }
990 return buf.toString();
991 }
992
993 }
994