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 ImageMetadata updateMetadata() { 299 ImageMetadata metaData = new ImageMetadata(null, true, 300 null, null, null, null, null, width, height, null, null, null); 301 updateImageMetadata(metaData); 302 return metaData; 303 } 304 305 private ImageStorage.ImageType getType() { 306 switch (colorType) { 307 case PNG_COLOR_GRAY: 308 return tRNS_present 309 ? ImageStorage.ImageType.GRAY_ALPHA 310 : ImageStorage.ImageType.GRAY; 311 case PNG_COLOR_RGB: 312 return tRNS_present 313 ? ImageStorage.ImageType.RGBA 314 : ImageStorage.ImageType.RGB; 315 case PNG_COLOR_PALETTE: 316 return ImageStorage.ImageType.PALETTE; 317 case PNG_COLOR_GRAY_ALPHA: 318 return ImageStorage.ImageType.GRAY_ALPHA; 319 case PNG_COLOR_RGB_ALPHA: 320 return ImageStorage.ImageType.RGBA; 321 default: // unreacheble 322 throw new RuntimeException(); 323 } 324 } 325 326 private void doSubFilter(byte line[], int bpp) { 327 int l = line.length; 328 for (int i = bpp; i != l; ++i) { 329 line[i] = (byte) (line[i] + line[i - bpp]); 330 } 331 } 332 333 private void doUpFilter(byte line[], byte pline[]) { 334 int l = line.length; 335 for (int i = 0; i != l; ++i) { 336 line[i] = (byte) (line[i] + pline[i]); 337 } 338 } 339 340 private void doAvrgFilter(byte line[], byte pline[], int bpp) { 341 int l = line.length; 342 for (int i = 0; i != bpp; ++i) { 343 line[i] = (byte) (line[i] + (pline[i] & 0xFF) / 2); 344 } 345 for (int i = bpp; i != l; ++i) { 346 line[i] = (byte) (line[i] 347 + (((line[i - bpp] & 0xFF) + (pline[i] & 0xFF))) / 2); 348 } 349 } 350 351 private static int paethPr(int a, int b, int c) { 352 // int p = a + b - c 353 int pa = Math.abs(b - c); // p-a 354 int pb = Math.abs(a - c); // p-b 355 int pc = Math.abs(b - c + a - c); // p-c 356 return (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; 357 } 358 359 private void doPaethFilter(byte line[], byte pline[], int bpp) { 360 int l = line.length; 361 for (int i = 0; i != bpp; ++i) { 362 line[i] = (byte) (line[i] + pline[i]); 363 } 364 for (int i = bpp; i != l; ++i) { 365 line[i] = (byte) (line[i] 366 + paethPr(line[i - bpp] & 0xFF, pline[i] & 0xFF, pline[i - bpp] & 0xFF)); 367 } 368 } 369 370 private void doFilter(byte line[], byte pline[], int fType, int bpp) { 371 switch (fType) { 372 case PNG_FILTER_SUB: 373 doSubFilter(line, bpp); 374 break; 375 case PNG_FILTER_UP: 376 doUpFilter(line, pline); 377 break; 378 case PNG_FILTER_AVERAGE: 379 doAvrgFilter(line, pline, bpp); 380 break; 381 case PNG_FILTER_PAETH: 382 doPaethFilter(line, pline, bpp); 383 break; 384 } 385 } 386 387 private void downsample16to8trns_gray(byte line[], byte image[], int pos, int step) { 388 int l = line.length / 2; 389 for (int i = 0, oPos = pos; i < l; oPos += step * 2, ++i) { 390 int gray16 = (short) ((line[i * 2] & 0xFF) * 256 + (line[i * 2 + 1] & 0xFF)); 391 image[oPos + 0] = line[i * 2]; 392 image[oPos + 1] = (gray16 == trnsG) ? 0 : (byte) 255; 393 } 394 } 395 396 private void downsample16to8trns_rgb(byte line[], byte image[], int pos, int step) { 397 int l = line.length / 2 / 3; 398 for (int i = 0, oPos = pos; i < l; oPos += step * 4, ++i) { 399 int iPos = i * 6; 400 int r16 = (short) ((line[iPos + 0] & 0xFF) * 256 + (line[iPos + 1] & 0xFF)); 401 int g16 = (short) ((line[iPos + 2] & 0xFF) * 256 + (line[iPos + 3] & 0xFF)); 402 int b16 = (short) ((line[iPos + 4] & 0xFF) * 256 + (line[iPos + 5] & 0xFF)); 403 image[oPos + 0] = line[iPos + 0]; 404 image[oPos + 1] = line[iPos + 2]; 405 image[oPos + 2] = line[iPos + 4]; 406 image[oPos + 3] = 407 (r16 == trnsR && g16 == trnsG && b16 == trnsB) ? 0 : (byte) 255; 408 } 409 } 410 411 private void downsample16to8_plain(byte line[], byte image[], int pos, int step, int bpp) { 412 int l = (line.length / 2 / bpp) * bpp, stepBpp = step * bpp; 413 for (int i = 0, oPos = pos; i != l; oPos += stepBpp, i += bpp) { 414 for (int b = 0; b != bpp; ++b) { 415 image[oPos + b] = line[(i + b) * 2]; 416 } 417 } 418 } 419 420 private void downsample16to8(byte line[], byte image[], int pos, int step, int bpp) { 421 if (!tRNS_GRAY_RGB) { 422 downsample16to8_plain(line, image, pos, step, bpp); 423 } else if (colorType == PNG_COLOR_GRAY) { 424 downsample16to8trns_gray(line, image, pos, step); 425 } else if (colorType == PNG_COLOR_RGB) { 426 downsample16to8trns_rgb(line, image, pos, step); 427 } 428 } 429 430 private void copyTrns_gray(byte line[], byte image[], int pos, int step) { 431 byte tG = (byte) trnsG; 432 for (int i = 0, oPos = pos, l = line.length; i < l; oPos += 2 * step, ++i) { 433 byte gray = line[i]; 434 image[oPos] = gray; 435 image[oPos + 1] = (gray == tG) ? 0 : (byte) 255; 436 } 437 } 438 439 private void copyTrns_rgb(byte line[], byte image[], int pos, int step) { 440 byte tR = (byte) trnsR, tG = (byte) trnsG, tB = (byte) trnsB; 441 int l = line.length / 3; 442 for (int i = 0, oPos = pos; i < l; oPos += step * 4, ++i) { 443 byte r = line[i * 3], g = line[i * 3 + 1], b = line[i * 3 + 2]; 444 image[oPos + 0] = r; 445 image[oPos + 1] = g; 446 image[oPos + 2] = b; 447 image[oPos + 3] = (r == tR && g == tG && b == tB) ? 0 : (byte) 255; 448 } 449 } 450 451 private void copy_plain(byte line[], byte image[], int pos, int step, int bpp) { 452 int l = line.length, stepBpp = step * bpp; 453 for (int i = 0, oPos = pos; i != l; oPos += stepBpp, i += bpp) { 454 for (int b = 0; b != bpp; ++b) { 455 image[oPos + b] = line[i + b]; 456 } 457 } 458 } 459 460 private void copy(byte line[], byte image[], int pos, int step, int resultBpp) { 461 if (!tRNS_GRAY_RGB) { 462 if (step == 1) { 463 System.arraycopy(line, 0, image, pos, line.length); 464 } else { 465 copy_plain(line, image, pos, step, resultBpp); 466 } 467 } else if (colorType == PNG_COLOR_GRAY) { 468 copyTrns_gray(line, image, pos, step); // resultBpp==2 469 } else if (colorType == PNG_COLOR_RGB) { 470 copyTrns_rgb(line, image, pos, step); // resultBpp==4 471 } 472 } 473 474 private void upsampleTo8Palette(byte line[], byte image[], int pos, int w, int step) { 475 int samplesInByte = 8 / bitDepth; 476 int maxV = (1 << bitDepth) - 1; 477 for (int i = 0, k = 0; i < w; k++, i += samplesInByte) { 478 int p = (w - i < samplesInByte) ? w - i : samplesInByte; 479 int in = line[k] >> (samplesInByte - p) * bitDepth; 480 for (int pp = p - 1; pp >= 0; --pp) { 481 image[pos + (i + pp) * step] = (byte) (in & maxV); 482 in >>= bitDepth; 483 } 484 } 485 } 486 487 private void upsampleTo8Gray(byte line[], byte image[], int pos, int w, int step) { 488 int samplesInByte = 8 / bitDepth; 489 int maxV = (1 << bitDepth) - 1, hmaxV = maxV / 2; 490 for (int i = 0, k = 0; i < w; k++, i += samplesInByte) { 491 int p = (w - i < samplesInByte) ? w - i : samplesInByte; 492 int in = line[k] >> (samplesInByte - p) * bitDepth; 493 for (int pp = p - 1; pp >= 0; --pp) { 494 image[pos + (i + pp) * step] = (byte) (((in & maxV) * 255 + hmaxV) / maxV); 495 in >>= bitDepth; 496 } 497 } 498 } 499 500 private void upsampleTo8GrayTrns(byte line[], byte image[], int pos, int w, int step) { 501 int samplesInByte = 8 / bitDepth; 502 int maxV = (1 << bitDepth) - 1, hmaxV = maxV / 2; 503 for (int i = 0, k = 0; i < w; k++, i += samplesInByte) { 504 int p = (w - i < samplesInByte) ? w - i : samplesInByte; 505 int in = line[k] >> (samplesInByte - p) * bitDepth; 506 for (int pp = p - 1; pp >= 0; --pp) { 507 int idx = pos + (i + pp) * step * 2; 508 int value = in & maxV; 509 image[idx] = (byte) ((value * 255 + hmaxV) / maxV); 510 image[idx + 1] = value == trnsG ? 0 : (byte) 255; 511 in >>= bitDepth; 512 } 513 } 514 } 515 516 private void upsampleTo8(byte line[], byte image[], int pos, int w, int step, int bpp) { 517 if (colorType == PNG_COLOR_PALETTE) { // as is decoder 518 upsampleTo8Palette(line, image, pos, w, step); 519 } else if (bpp == 1) { 520 upsampleTo8Gray(line, image, pos, w, step); 521 } else if (tRNS_GRAY_RGB && bpp == 2) { 522 upsampleTo8GrayTrns(line, image, pos, w, step); 523 } 524 } 525 526 private static final int starting_y[] = {0, 0, 4, 0, 2, 0, 1, 0}; 527 private static final int starting_x[] = {0, 4, 0, 2, 0, 1, 0, 0}; 528 private static final int increment_y[] = {8, 8, 8, 4, 4, 2, 2, 1}; 529 private static final int increment_x[] = {8, 8, 4, 4, 2, 2, 1, 1}; 530 531 private static int mipSize(int size, int mip, int start[], int increment[]) { 532 return (size - start[mip] + increment[mip] - 1) / increment[mip]; 533 } 534 535 private static int mipPos(int pos, int mip, int start[], int increment[]) { 536 return start[mip] + pos * increment[mip]; 537 } 538 539 private void loadMip(byte image[], InputStream data, int mip) throws IOException { 540 541 int mipWidth = mipSize(width, mip, starting_x, increment_x); 542 int mipHeight = mipSize(height, mip, starting_y, increment_y); 543 544 int scanLineSize = (mipWidth * bitDepth * numBandsPerColorType[colorType] + 7) / 8; 545 byte scanLine0[] = new byte[scanLineSize]; 546 byte scanLine1[] = new byte[scanLineSize]; 547 548 // numBands might be more than numBandsPerColorType[colorType] 549 // to support tRNS 550 int resultBpp = bpp(), srcBpp = numBandsPerColorType[colorType] * bytesPerColor(); 551 552 for (int y = 0; y != mipHeight; ++y) { 553 int filterByte = data.read(); 554 if (filterByte == -1) { 555 throw new EOFException(); 556 } 557 558 if (data.read(scanLine0) != scanLineSize) { 559 throw new EOFException(); 560 } 561 562 doFilter(scanLine0, scanLine1, filterByte, srcBpp); 563 564 int pos = (mipPos(y, mip, starting_y, increment_y) * width + starting_x[mip]) * resultBpp; 565 int step = increment_x[mip]; 566 567 if (bitDepth == 16) { 568 downsample16to8(scanLine0, image, pos, step, resultBpp); 569 } else if (bitDepth < 8) { 570 upsampleTo8(scanLine0, image, pos, mipWidth, step, resultBpp); 571 } else { 572 copy(scanLine0, image, pos, step, resultBpp); 573 } 574 575 byte scanLineSwp[] = scanLine0; 576 scanLine0 = scanLine1; 577 scanLine1 = scanLineSwp; 578 } 579 } 580 581 private void load(byte image[], InputStream data) throws IOException { 582 if (isInterlaced) { 583 for (int mip = 0; mip != 7; ++mip) { 584 if (width > starting_x[mip] && height > starting_y[mip]) { 585 loadMip(image, data, mip); 586 } 587 } 588 } else { 589 loadMip(image, data, 7); 590 } 591 } 592 593 private ImageFrame decodePalette(byte srcImage[], ImageMetadata metadata) { 594 int bpp = tRNS_present ? 4 : 3; 595 byte newImage[] = new byte[width * height * bpp]; 596 int l = width * height; 597 598 if (tRNS_present) { 599 for (int i = 0, j = 0; i != l; j += 4, i++) { 600 int index = 0xFF & srcImage[i]; 601 newImage[j + 0] = palette[0][index]; 602 newImage[j + 1] = palette[1][index]; 603 newImage[j + 2] = palette[2][index]; 604 newImage[j + 3] = palette[3][index]; 605 } 606 } else { 607 for (int i = 0, j = 0; i != l; j += 3, i++) { 608 int index = 0xFF & srcImage[i]; 609 newImage[j + 0] = palette[0][index]; 610 newImage[j + 1] = palette[1][index]; 611 newImage[j + 2] = palette[2][index]; 612 } 613 } 614 615 ImageStorage.ImageType type = tRNS_present 616 ? ImageStorage.ImageType.RGBA 617 : ImageStorage.ImageType.RGB; 618 619 return new ImageFrame(type, ByteBuffer.wrap(newImage), width, height, 620 width * bpp, null, metadata); 621 } 622 623 // we won`t decode palette on fly, we will do it later 624 // it is possible that we might want original paletteized image 625 // ImageFrame does not support 16 bit color depth, 626 // numBandsPerColorType == bytesPerColorType 627 // but we will convert RGB->RGBA and L->LA on order to support tRNS 628 private int bpp() { 629 return numBandsPerColorType[colorType] + (tRNS_GRAY_RGB ? 1 : 0); 630 } 631 632 private int bytesPerColor() { 633 return bitDepth == 16 ? 2 : 1; 634 } 635 636 public ImageFrame load(int imageIndex, int rWidth, int rHeight, 637 boolean preserveAspectRatio, boolean smooth) throws IOException { 638 639 if (imageIndex != 0) { 640 return null; 641 } 642 643 int dataSize = parsePngMeta(); 644 645 if (dataSize == 0) { 646 emitWarning("No image data in PNG"); 647 return null; 648 } 649 650 int bpp = bpp(); 651 ByteBuffer bb = ByteBuffer.allocate(bpp * width * height); 652 ImageMetadata metadata = updateMetadata(); 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 // need remove scaler form loader 673 int[] outWH = ImageTools.computeDimensions(width, height, rWidth, rHeight, preserveAspectRatio); 674 675 if (width != outWH[0] || height != outWH[1]) { 676 imgPNG = scaleImage(imgPNG, outWH[0], outWH[1], smooth); 677 } 678 679 return imgPNG; 680 } 681 682 private ImageFrame scaleImage(ImageFrame imgPNG, int rWidth, int rHeight, boolean smooth) { 683 byte image[] = ((ByteBuffer) imgPNG.getImageData()).array(); 684 int bpp = ImageStorage.getNumBands(imgPNG.getImageType()); 685 686 PushbroomScaler scaler = ScalerFactory.createScaler(width, height, bpp, 687 rWidth, rHeight, smooth); 688 689 for (int y = 0; y != height; ++y) { 690 scaler.putSourceScanline(image, y * width * bpp); 691 } 692 693 return new ImageFrame(imgPNG.getImageType(), scaler.getDestination(), 694 rWidth, rHeight, rWidth * bpp, null, imgPNG.getMetadata()); 695 } 696 }