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 }