1 /*
   2  * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * loader implementation for PNG file format
  28  * specification http://www.w3.org/TR/PNG/
  29  */
  30 package com.sun.javafx.iio.png;
  31 
  32 import com.sun.javafx.iio.*;
  33 import com.sun.javafx.iio.common.*;
  34 import java.io.*;
  35 import java.nio.ByteBuffer;
  36 import java.util.Arrays;
  37 import java.util.zip.*;
  38 
  39 public final class PNGImageLoader2 extends ImageLoaderImpl {
  40 
  41     // file signature
  42     static final byte FILE_SIG[] = {(byte) 137, (byte) 80, (byte) 78,
  43         (byte) 71, (byte) 13, (byte) 10, (byte) 26, (byte) 10};
  44     // Critical chunks
  45     static final int IHDR_TYPE = 0x49484452;
  46     static final int PLTE_TYPE = 0x504c5445;
  47     static final int IDAT_TYPE = 0x49444154;
  48     static final int IEND_TYPE = 0x49454e44;
  49     // Ancillary chunks
  50     static final int tRNS_TYPE = 0x74524e53;
  51     // color model
  52     static final int PNG_COLOR_GRAY = 0;
  53     static final int PNG_COLOR_RGB = 2;
  54     static final int PNG_COLOR_PALETTE = 3;
  55     static final int PNG_COLOR_GRAY_ALPHA = 4;
  56     static final int PNG_COLOR_RGB_ALPHA = 6;
  57     // channels per pixel
  58     static final int[] numBandsPerColorType = {1, -1, 3, 1, 2, -1, 4};
  59     // filters
  60     static final int PNG_FILTER_NONE = 0;
  61     static final int PNG_FILTER_SUB = 1;
  62     static final int PNG_FILTER_UP = 2;
  63     static final int PNG_FILTER_AVERAGE = 3;
  64     static final int PNG_FILTER_PAETH = 4;
  65     // data stream
  66     private final DataInputStream stream;
  67     private int width, height, bitDepth, colorType;
  68     private boolean isInterlaced;
  69     // transparency information
  70     private boolean tRNS_present = false;
  71     private boolean tRNS_GRAY_RGB = false;
  72     private int trnsR, trnsG, trnsB;
  73     // Palette data : r,g,b,[a]  -  alpha optional
  74     private byte palette[][];
  75 
  76     public PNGImageLoader2(InputStream input) throws IOException {
  77         super(PNGDescriptor.getInstance());
  78         stream = new DataInputStream(input);
  79 
  80         byte signature[] = readBytes(new byte[8]);
  81 
  82         if (!Arrays.equals(FILE_SIG, signature)) {
  83             throw new IOException("Bad PNG signature!");
  84         }
  85 
  86         readHeader();
  87     }
  88 
  89     private void readHeader() throws IOException {
  90         int hdrData[] = readChunk();
  91 
  92         if (hdrData[1] != IHDR_TYPE && hdrData[0] != 13) {
  93             throw new IOException("Bad PNG header!");
  94         }
  95 
  96         width = stream.readInt();
  97         height = stream.readInt();
  98 
  99         if (width == 0 || height == 0) {
 100             throw new IOException("Bad PNG image size!");
 101         }
 102 
 103         bitDepth = stream.readByte();
 104         if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4
 105                 && bitDepth != 8 && bitDepth != 16) {
 106             throw new IOException("Bad PNG bit depth");
 107         }
 108 
 109         colorType = stream.readByte();
 110 
 111         if (colorType > 6 || colorType == 1 || colorType == 5) {
 112             throw new IOException("Bad PNG color type");
 113         }
 114 
 115         // bitDepth<8 only for palette and gray
 116         // bitDepth==16 not for palette
 117         if ((colorType != PNG_COLOR_PALETTE && colorType != PNG_COLOR_GRAY && bitDepth < 8)
 118                 || (colorType == PNG_COLOR_PALETTE && bitDepth == 16)) {
 119             throw new IOException("Bad color type/bit depth combination!");
 120         }
 121 
 122         byte compressionMethod = stream.readByte();
 123         if (compressionMethod != 0) {
 124             throw new IOException("Bad PNG comression!");
 125         }
 126 
 127         byte filterMethod = stream.readByte();
 128         if (filterMethod != 0) {
 129             throw new IOException("Bad PNG filter method!");
 130         }
 131 
 132         byte interlaceMethod = stream.readByte();
 133 
 134         if (interlaceMethod != 0 && interlaceMethod != 1) {
 135             throw new IOException("Unknown interlace method (not 0 or 1)!");
 136         }
 137 
 138         int crc = stream.readInt();
 139 
 140         isInterlaced = interlaceMethod == 1;
 141     }
 142 
 143     private int[] readChunk() throws IOException {
 144         return new int[]{stream.readInt(), stream.readInt()};
 145     }
 146 
 147     private byte[] readBytes(byte data[]) throws IOException {
 148         return readBytes(data, 0, data.length);
 149     }
 150 
 151     private byte[] readBytes(byte data[], int offs, int size) throws IOException {
 152         stream.readFully(data, offs, size);
 153         return data;
 154     }
 155 
 156     private void skip(int n) throws IOException {
 157         if (n != stream.skipBytes(n)) {
 158             throw new EOFException();
 159         }
 160     }
 161 
 162     private void readPaletteChunk(int chunkLength) throws IOException {
 163         int numEntries = chunkLength / 3;
 164         int paletteEntries = 1 << bitDepth;
 165         if (numEntries > paletteEntries) {
 166             emitWarning("PLTE chunk contains too many entries for bit depth, ignoring extras.");
 167             numEntries = paletteEntries;
 168         }
 169 
 170         palette = new byte[3][paletteEntries];
 171 
 172         byte paletteData[] = readBytes(new byte[chunkLength]);
 173 
 174         for (int i = 0, idx = 0; i != numEntries; ++i) {
 175             for (int k = 0; k != 3; ++k) {
 176                 palette[k][i] = paletteData[idx++];
 177             }
 178         }
 179     }
 180 
 181     private void parsePaletteChunk(int chunkLength) throws IOException {
 182         if (palette != null) {
 183             emitWarning(
 184                     "A PNG image may not contain more than one PLTE chunk.\n"
 185                     + "The chunk wil be ignored.");
 186             skip(chunkLength);
 187             return;
 188         }
 189 
 190         switch (colorType) {
 191             case PNG_COLOR_PALETTE:
 192                 readPaletteChunk(chunkLength);
 193                 return;
 194             case PNG_COLOR_GRAY:
 195             case PNG_COLOR_GRAY_ALPHA:
 196                 emitWarning("A PNG gray or gray alpha image cannot have a PLTE chunk.\n"
 197                         + "The chunk wil be ignored.");
 198             // silently ignore palette for RGB
 199             default:
 200                 skip(chunkLength);
 201         }
 202     }
 203 
 204     private boolean readPaletteTransparency(int chunkLength) throws IOException {
 205         if (palette == null) {
 206             emitWarning("tRNS chunk without prior PLTE chunk, ignoring it.");
 207             skip(chunkLength);
 208             return false;
 209         }
 210 
 211         byte newPal[][] = new byte[4][];
 212 
 213         System.arraycopy(palette, 0, newPal, 0, 3);
 214 
 215         int paletteLength = palette[0].length;
 216         newPal[3] = new byte[paletteLength];
 217 
 218         int nRead = chunkLength < paletteLength ? chunkLength : paletteLength;
 219         readBytes(newPal[3], 0, nRead);
 220 
 221         for (int i = nRead; i < paletteLength; ++i) {
 222             newPal[3][i] = -1;
 223         }
 224 
 225         if (nRead < chunkLength) {
 226             skip(chunkLength - nRead);
 227         }
 228 
 229         palette = newPal;
 230 
 231         return true;
 232     }
 233 
 234     private boolean readGrayTransparency(int chunkLength) throws IOException {
 235         if (chunkLength == 2) {
 236             trnsG = stream.readShort();
 237             return true;
 238         }
 239         return false;
 240     }
 241 
 242     private boolean readRgbTransparency(int chunkLength) throws IOException {
 243         if (chunkLength == 6) {
 244             trnsR = stream.readShort();
 245             trnsG = stream.readShort();
 246             trnsB = stream.readShort();
 247             return true;
 248         }
 249         return false;
 250     }
 251 
 252     private void parseTransparencyChunk(int chunkLength) throws IOException {
 253         switch (colorType) {
 254             case PNG_COLOR_PALETTE:
 255                 tRNS_present = readPaletteTransparency(chunkLength);
 256                 break;
 257             case PNG_COLOR_GRAY:
 258                 tRNS_GRAY_RGB = tRNS_present = readGrayTransparency(chunkLength);
 259                 break;
 260             case PNG_COLOR_RGB:
 261                 tRNS_GRAY_RGB = tRNS_present = readRgbTransparency(chunkLength);
 262                 break;
 263             default:
 264                 emitWarning("TransparencyChunk may not present when alpha explicitly defined");
 265                 skip(chunkLength);
 266         }
 267     }
 268 
 269     // return sizeof first IDAT chunk or 0 of error
 270     private int parsePngMeta() throws IOException {
 271         while (true) {
 272             int chunk[] = readChunk();
 273 
 274             if (chunk[0] < 0) {
 275                 throw new IOException("Invalid chunk length");
 276             }
 277             switch (chunk[1]) {
 278                 case IDAT_TYPE:
 279                     return chunk[0];
 280                 case IEND_TYPE:
 281                     return 0;
 282                 case PLTE_TYPE:
 283                     parsePaletteChunk(chunk[0]);
 284                     break;
 285                 case tRNS_TYPE:
 286                     parseTransparencyChunk(chunk[0]);
 287                     break;
 288                 default:
 289                     skip(chunk[0]);
 290             }
 291             int crc = stream.readInt();
 292         }
 293     }
 294 
 295     public void dispose() {
 296     }
 297 
 298     private ImageStorage.ImageType getType() {
 299         switch (colorType) {
 300             case PNG_COLOR_GRAY:
 301                 return tRNS_present
 302                         ? ImageStorage.ImageType.GRAY_ALPHA
 303                         : ImageStorage.ImageType.GRAY;
 304             case PNG_COLOR_RGB:
 305                 return tRNS_present
 306                         ? ImageStorage.ImageType.RGBA
 307                         : ImageStorage.ImageType.RGB;
 308             case PNG_COLOR_PALETTE:
 309                 return ImageStorage.ImageType.PALETTE;
 310             case PNG_COLOR_GRAY_ALPHA:
 311                 return ImageStorage.ImageType.GRAY_ALPHA;
 312             case PNG_COLOR_RGB_ALPHA:
 313                 return ImageStorage.ImageType.RGBA;
 314             default: // unreacheble
 315                 throw new RuntimeException();
 316         }
 317     }
 318 
 319     private void doSubFilter(byte line[], int bpp) {
 320         int l = line.length;
 321         for (int i = bpp; i != l; ++i) {
 322             line[i] = (byte) (line[i] + line[i - bpp]);
 323         }
 324     }
 325 
 326     private void doUpFilter(byte line[], byte pline[]) {
 327         int l = line.length;
 328         for (int i = 0; i != l; ++i) {
 329             line[i] = (byte) (line[i] + pline[i]);
 330         }
 331     }
 332 
 333     private void doAvrgFilter(byte line[], byte pline[], int bpp) {
 334         int l = line.length;
 335         for (int i = 0; i != bpp; ++i) {
 336             line[i] = (byte) (line[i] + (pline[i] & 0xFF) / 2);
 337         }
 338         for (int i = bpp; i != l; ++i) {
 339             line[i] = (byte) (line[i]
 340                     + (((line[i - bpp] & 0xFF) + (pline[i] & 0xFF))) / 2);
 341         }
 342     }
 343 
 344     private static int paethPr(int a, int b, int c) {
 345         // int p = a + b - c
 346         int pa = Math.abs(b - c);      // p-a
 347         int pb = Math.abs(a - c);      // p-b
 348         int pc = Math.abs(b - c + a - c);  // p-c
 349         return (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c;
 350     }
 351 
 352     private void doPaethFilter(byte line[], byte pline[], int bpp) {
 353         int l = line.length;
 354         for (int i = 0; i != bpp; ++i) {
 355             line[i] = (byte) (line[i] + pline[i]);
 356         }
 357         for (int i = bpp; i != l; ++i) {
 358             line[i] = (byte) (line[i]
 359                     + paethPr(line[i - bpp] & 0xFF, pline[i] & 0xFF, pline[i - bpp] & 0xFF));
 360         }
 361     }
 362 
 363     private void doFilter(byte line[], byte pline[], int fType, int bpp) {
 364         switch (fType) {
 365             case PNG_FILTER_SUB:
 366                 doSubFilter(line, bpp);
 367                 break;
 368             case PNG_FILTER_UP:
 369                 doUpFilter(line, pline);
 370                 break;
 371             case PNG_FILTER_AVERAGE:
 372                 doAvrgFilter(line, pline, bpp);
 373                 break;
 374             case PNG_FILTER_PAETH:
 375                 doPaethFilter(line, pline, bpp);
 376                 break;
 377         }
 378     }
 379 
 380     private void downsample16to8trns_gray(byte line[], byte image[], int pos, int step) {
 381         int l = line.length / 2;
 382         for (int i = 0, oPos = pos; i < l; oPos += step * 2, ++i) {
 383             int gray16 = (short) ((line[i * 2] & 0xFF) * 256 + (line[i * 2 + 1] & 0xFF));
 384             image[oPos + 0] = line[i * 2];
 385             image[oPos + 1] = (gray16 == trnsG) ? 0 : (byte) 255;
 386         }
 387     }
 388 
 389     private void downsample16to8trns_rgb(byte line[], byte image[], int pos, int step) {
 390         int l = line.length / 2 / 3;
 391         for (int i = 0, oPos = pos; i < l; oPos += step * 4, ++i) {
 392             int iPos = i * 6;
 393             int r16 = (short) ((line[iPos + 0] & 0xFF) * 256 + (line[iPos + 1] & 0xFF));
 394             int g16 = (short) ((line[iPos + 2] & 0xFF) * 256 + (line[iPos + 3] & 0xFF));
 395             int b16 = (short) ((line[iPos + 4] & 0xFF) * 256 + (line[iPos + 5] & 0xFF));
 396             image[oPos + 0] = line[iPos + 0];
 397             image[oPos + 1] = line[iPos + 2];
 398             image[oPos + 2] = line[iPos + 4];
 399             image[oPos + 3] =
 400                     (r16 == trnsR && g16 == trnsG && b16 == trnsB) ? 0 : (byte) 255;
 401         }
 402     }
 403 
 404     private void downsample16to8_plain(byte line[], byte image[], int pos, int step, int bpp) {
 405         int l = (line.length / 2 / bpp) * bpp, stepBpp = step * bpp;
 406         for (int i = 0, oPos = pos; i != l; oPos += stepBpp, i += bpp) {
 407             for (int b = 0; b != bpp; ++b) {
 408                 image[oPos + b] = line[(i + b) * 2];
 409             }
 410         }
 411     }
 412 
 413     private void downsample16to8(byte line[], byte image[], int pos, int step, int bpp) {
 414         if (!tRNS_GRAY_RGB) {
 415             downsample16to8_plain(line, image, pos, step, bpp);
 416         } else if (colorType == PNG_COLOR_GRAY) {
 417             downsample16to8trns_gray(line, image, pos, step);
 418         } else if (colorType == PNG_COLOR_RGB) {
 419             downsample16to8trns_rgb(line, image, pos, step);
 420         }
 421     }
 422 
 423     private void copyTrns_gray(byte line[], byte image[], int pos, int step) {
 424         byte tG = (byte) trnsG;
 425         for (int i = 0, oPos = pos, l = line.length; i < l; oPos += 2 * step, ++i) {
 426             byte gray = line[i];
 427             image[oPos] = gray;
 428             image[oPos + 1] = (gray == tG) ? 0 : (byte) 255;
 429         }
 430     }
 431 
 432     private void copyTrns_rgb(byte line[], byte image[], int pos, int step) {
 433         byte tR = (byte) trnsR, tG = (byte) trnsG, tB = (byte) trnsB;
 434         int l = line.length / 3;
 435         for (int i = 0, oPos = pos; i < l; oPos += step * 4, ++i) {
 436             byte r = line[i * 3], g = line[i * 3 + 1], b = line[i * 3 + 2];
 437             image[oPos + 0] = r;
 438             image[oPos + 1] = g;
 439             image[oPos + 2] = b;
 440             image[oPos + 3] = (r == tR && g == tG && b == tB) ? 0 : (byte) 255;
 441         }
 442     }
 443 
 444     private void copy_plain(byte line[], byte image[], int pos, int step, int bpp) {
 445         int l = line.length, stepBpp = step * bpp;
 446         for (int i = 0, oPos = pos; i != l; oPos += stepBpp, i += bpp) {
 447             for (int b = 0; b != bpp; ++b) {
 448                 image[oPos + b] = line[i + b];
 449             }
 450         }
 451     }
 452 
 453     private void copy(byte line[], byte image[], int pos, int step, int resultBpp) {
 454         if (!tRNS_GRAY_RGB) {
 455             if (step == 1) {
 456                 System.arraycopy(line, 0, image, pos, line.length);
 457             } else {
 458                 copy_plain(line, image, pos, step, resultBpp);
 459             }
 460         } else if (colorType == PNG_COLOR_GRAY) {
 461             copyTrns_gray(line, image, pos, step); // resultBpp==2
 462         } else if (colorType == PNG_COLOR_RGB) {
 463             copyTrns_rgb(line, image, pos, step); // resultBpp==4
 464         }
 465     }
 466 
 467     private void upsampleTo8Palette(byte line[], byte image[], int pos, int w, int step) {
 468         int samplesInByte = 8 / bitDepth;
 469         int maxV = (1 << bitDepth) - 1;
 470         for (int i = 0, k = 0; i < w; k++, i += samplesInByte) {
 471             int p = (w - i < samplesInByte) ? w - i : samplesInByte;
 472             int in = line[k] >> (samplesInByte - p) * bitDepth;
 473             for (int pp = p - 1; pp >= 0; --pp) {
 474                 image[pos + (i + pp) * step] = (byte) (in & maxV);
 475                 in >>= bitDepth;
 476             }
 477         }
 478     }
 479 
 480     private void upsampleTo8Gray(byte line[], byte image[], int pos, int w, int step) {
 481         int samplesInByte = 8 / bitDepth;
 482         int maxV = (1 << bitDepth) - 1, hmaxV = maxV / 2;
 483         for (int i = 0, k = 0; i < w; k++, i += samplesInByte) {
 484             int p = (w - i < samplesInByte) ? w - i : samplesInByte;
 485             int in = line[k] >> (samplesInByte - p) * bitDepth;
 486             for (int pp = p - 1; pp >= 0; --pp) {
 487                 image[pos + (i + pp) * step] = (byte) (((in & maxV) * 255 + hmaxV) / maxV);
 488                 in >>= bitDepth;
 489             }
 490         }
 491     }
 492 
 493     private void upsampleTo8GrayTrns(byte line[], byte image[], int pos, int w, int step) {
 494         int samplesInByte = 8 / bitDepth;
 495         int maxV = (1 << bitDepth) - 1, hmaxV = maxV / 2;
 496         for (int i = 0, k = 0; i < w; k++, i += samplesInByte) {
 497             int p = (w - i < samplesInByte) ? w - i : samplesInByte;
 498             int in = line[k] >> (samplesInByte - p) * bitDepth;
 499             for (int pp = p - 1; pp >= 0; --pp) {
 500                 int idx = pos + (i + pp) * step * 2;
 501                 int value = in & maxV;
 502                 image[idx] = (byte) ((value * 255 + hmaxV) / maxV);
 503                 image[idx + 1] = value == trnsG ? 0 : (byte) 255;
 504                 in >>= bitDepth;
 505             }
 506         }
 507     }
 508 
 509     private void upsampleTo8(byte line[], byte image[], int pos, int w, int step, int bpp) {
 510         if (colorType == PNG_COLOR_PALETTE) { // as is decoder
 511             upsampleTo8Palette(line, image, pos, w, step);
 512         } else if (bpp == 1) {
 513             upsampleTo8Gray(line, image, pos, w, step);
 514         } else if (tRNS_GRAY_RGB && bpp == 2) {
 515             upsampleTo8GrayTrns(line, image, pos, w, step);
 516         }
 517     }
 518 
 519     private static final int starting_y[] = {0, 0, 4, 0, 2, 0, 1, 0};
 520     private static final int starting_x[] = {0, 4, 0, 2, 0, 1, 0, 0};
 521     private static final int increment_y[] = {8, 8, 8, 4, 4, 2, 2, 1};
 522     private static final int increment_x[] = {8, 8, 4, 4, 2, 2, 1, 1};
 523 
 524     private static int mipSize(int size, int mip, int start[], int increment[]) {
 525         return (size - start[mip] + increment[mip] - 1) / increment[mip];
 526     }
 527 
 528     private static int mipPos(int pos, int mip, int start[], int increment[]) {
 529         return start[mip] + pos * increment[mip];
 530     }
 531 
 532     private void loadMip(byte image[], InputStream data, int mip) throws IOException {
 533 
 534         int mipWidth = mipSize(width, mip, starting_x, increment_x);
 535         int mipHeight = mipSize(height, mip, starting_y, increment_y);
 536 
 537         int scanLineSize = (mipWidth * bitDepth * numBandsPerColorType[colorType] + 7) / 8;
 538         byte scanLine0[] = new byte[scanLineSize];
 539         byte scanLine1[] = new byte[scanLineSize];
 540 
 541         // numBands might be more than numBandsPerColorType[colorType]
 542         // to support tRNS
 543         int resultBpp = bpp(), srcBpp = numBandsPerColorType[colorType] * bytesPerColor();
 544 
 545         for (int y = 0; y != mipHeight; ++y) {
 546             int filterByte = data.read();
 547             if (filterByte == -1) {
 548                 throw new EOFException();
 549             }
 550 
 551             if (data.read(scanLine0) != scanLineSize) {
 552                 throw new EOFException();
 553             }
 554 
 555             doFilter(scanLine0, scanLine1, filterByte, srcBpp);
 556 
 557             int pos = (mipPos(y, mip, starting_y, increment_y) * width + starting_x[mip]) * resultBpp;
 558             int step = increment_x[mip];
 559 
 560             if (bitDepth == 16) {
 561                 downsample16to8(scanLine0, image, pos, step, resultBpp);
 562             } else if (bitDepth < 8) {
 563                 upsampleTo8(scanLine0, image, pos, mipWidth, step, resultBpp);
 564             } else {
 565                 copy(scanLine0, image, pos, step, resultBpp);
 566             }
 567 
 568             byte scanLineSwp[] = scanLine0;
 569             scanLine0 = scanLine1;
 570             scanLine1 = scanLineSwp;
 571         }
 572     }
 573 
 574     private void load(byte image[], InputStream data) throws IOException {
 575         if (isInterlaced) {
 576             for (int mip = 0; mip != 7; ++mip) {
 577                 if (width > starting_x[mip] && height > starting_y[mip]) {
 578                     loadMip(image, data, mip);
 579                 }
 580             }
 581         } else {
 582             loadMip(image, data, 7);
 583         }
 584     }
 585 
 586     private ImageFrame decodePalette(byte srcImage[], ImageMetadata metadata) {
 587         int bpp = tRNS_present ? 4 : 3;
 588         byte newImage[] = new byte[width * height * bpp];
 589         int l = width * height;
 590 
 591         if (tRNS_present) {
 592             for (int i = 0, j = 0; i != l; j += 4, i++) {
 593                 int index = 0xFF & srcImage[i];
 594                 newImage[j + 0] = palette[0][index];
 595                 newImage[j + 1] = palette[1][index];
 596                 newImage[j + 2] = palette[2][index];
 597                 newImage[j + 3] = palette[3][index];
 598             }
 599         } else {
 600             for (int i = 0, j = 0; i != l; j += 3, i++) {
 601                 int index = 0xFF & srcImage[i];
 602                 newImage[j + 0] = palette[0][index];
 603                 newImage[j + 1] = palette[1][index];
 604                 newImage[j + 2] = palette[2][index];
 605             }
 606         }
 607 
 608         ImageStorage.ImageType type = tRNS_present
 609                 ? ImageStorage.ImageType.RGBA
 610                 : ImageStorage.ImageType.RGB;
 611 
 612         return new ImageFrame(type, ByteBuffer.wrap(newImage), width, height,
 613                 width * bpp, null, metadata);
 614     }
 615 
 616     // we won`t decode palette on fly, we will do it later
 617     // it is possible that we might want original paletteized image
 618     // ImageFrame does not support 16 bit color depth,
 619     // numBandsPerColorType == bytesPerColorType
 620     // but we will convert RGB->RGBA and L->LA on order to support tRNS
 621     private int bpp() {
 622         return numBandsPerColorType[colorType] + (tRNS_GRAY_RGB ? 1 : 0);
 623     }
 624 
 625     private int bytesPerColor() {
 626         return bitDepth == 16 ? 2 : 1;
 627     }
 628 
 629     public ImageFrame load(int imageIndex, int rWidth, int rHeight,
 630             boolean preserveAspectRatio, boolean smooth) throws IOException {
 631 
 632         if (imageIndex != 0) {
 633             return null;
 634         }
 635 
 636         int dataSize = parsePngMeta();
 637 
 638         if (dataSize == 0) {
 639             emitWarning("No image data in PNG");
 640             return null;
 641         }
 642 
 643         int[] outWH = ImageTools.computeDimensions(width, height, rWidth, rHeight, preserveAspectRatio);
 644         rWidth = outWH[0];
 645         rHeight = outWH[1];
 646         
 647         ImageMetadata metaData = new ImageMetadata(null, true,
 648                 null, null, null, null, null, rWidth, rHeight, null, null, null);
 649         updateImageMetadata(metaData);
 650 
 651         int bpp = bpp();
 652         ByteBuffer bb = ByteBuffer.allocate(bpp * width * height);
 653 
 654         PNGIDATChunkInputStream iDat = new PNGIDATChunkInputStream(stream, dataSize);
 655         Inflater inf = new Inflater();
 656         InputStream data = new BufferedInputStream(new InflaterInputStream(iDat, inf));
 657 
 658         try {
 659             load(bb.array(), data);
 660         } catch (IOException e) {
 661             throw e;
 662         } finally {
 663             if (inf != null) {
 664                 inf.end();
 665             }
 666         }
 667 
 668         ImageFrame imgPNG = colorType == PNG_COLOR_PALETTE
 669                 ? decodePalette(bb.array(), metaData)
 670                 : new ImageFrame(getType(), bb, width, height, bpp * width, palette, metaData);
 671 
 672         if (width != rWidth || height != rHeight) {
 673             imgPNG = ImageTools.scaleImageFrame(imgPNG, rWidth, rHeight, smooth);
 674         }
 675 
 676         return imgPNG;
 677     }
 678 }