681 stream.readFully(b);
682 metadata.zTXt_text.add(new String(inflate(b), "ISO-8859-1"));
683
684 // Check if the text chunk contains image creation time
685 if (keyword.equals(PNGMetadata.tEXt_creationTimeKey)) {
686 // Update Standard/Document/ImageCreationTime from text chunk
687 int index = metadata.zTXt_text.size() - 1;
688 metadata.decodeImageCreationTimeFromTextChunk(
689 metadata.zTXt_text.listIterator(index));
690 }
691 }
692
693 private void readMetadata() throws IIOException {
694 if (gotMetadata) {
695 return;
696 }
697
698 readHeader();
699
700 /*
701 * Optimization: We can skip the remaining metadata if the
702 * ignoreMetadata flag is set, and only if this is not a palette
703 * image (in that case, we need to read the metadata to get the
704 * tRNS chunk, which is needed for the getImageTypes() method).
705 */
706 int colorType = metadata.IHDR_colorType;
707 if (ignoreMetadata && colorType != PNG_COLOR_PALETTE) {
708 try {
709 while (true) {
710 int chunkLength = stream.readInt();
711
712 // verify the chunk length first
713 if (chunkLength < 0 || chunkLength + 4 < 0) {
714 throw new IIOException("Invalid chunk length " + chunkLength);
715 }
716
717 int chunkType = stream.readInt();
718
719 if (chunkType == IDAT_TYPE) {
720 // We've reached the image data
721 stream.skipBytes(-8);
722 imageStartPosition = stream.getStreamPosition();
723 break;
724 } else {
725 // Skip the chunk plus the 4 CRC bytes that follow
726 stream.skipBytes(chunkLength + 4);
727 }
728 }
729 } catch (IOException e) {
730 throw new IIOException("Error skipping PNG metadata", e);
731 }
732
733 gotMetadata = true;
734 return;
735 }
736
737 try {
738 loop: while (true) {
739 int chunkLength = stream.readInt();
740 int chunkType = stream.readInt();
741 int chunkCRC;
742
743 // verify the chunk length
1249 idx += 2;
1250 }
1251 }
1252
1253 // True Y position in source
1254 int sourceY = srcY*yStep + yStart;
1255 if ((sourceY >= sourceRegion.y) &&
1256 (sourceY < sourceRegion.y + sourceRegion.height) &&
1257 (((sourceY - sourceRegion.y) %
1258 sourceYSubsampling) == 0)) {
1259
1260 int dstY = destinationOffset.y +
1261 (sourceY - sourceRegion.y)/sourceYSubsampling;
1262 if (dstY < dstMinY) {
1263 continue;
1264 }
1265 if (dstY > dstMaxY) {
1266 break;
1267 }
1268
1269 if (useSetRect) {
1270 imRas.setRect(updateMinX, dstY, passRow);
1271 } else {
1272 int newSrcX = srcX;
1273
1274 for (int dstX = updateMinX;
1275 dstX < updateMinX + updateWidth;
1276 dstX += updateXStep) {
1277
1278 passRow.getPixel(newSrcX, 0, ps);
1279 if (adjustBitDepths) {
1280 for (int b = 0; b < numBands; b++) {
1281 ps[b] = scale[b][ps[b]];
1282 }
1283 }
1284 imRas.setPixel(dstX, dstY, ps);
1285 newSrcX += srcXStep;
1286 }
1287 }
1288
1289 processImageUpdate(theImage,
1290 updateMinX, dstY,
1291 updateWidth, 1,
1292 updateXStep, updateYStep,
1293 destinationBands);
1294 }
1295 }
1296
1297 processPassComplete(theImage);
1298 }
1299
1300 private void decodeImage()
1301 throws IOException, IIOException {
1302 int width = metadata.IHDR_width;
1303 int height = metadata.IHDR_height;
1304
1405 * the level of application, so we will not try to estimate
1406 * the required amount of the memory and/or handle OOM in
1407 * any way.
1408 */
1409 theImage = getDestination(param,
1410 getImageTypes(0),
1411 width,
1412 height);
1413
1414 Rectangle destRegion = new Rectangle(0, 0, 0, 0);
1415 sourceRegion = new Rectangle(0, 0, 0, 0);
1416 computeRegions(param, width, height,
1417 theImage,
1418 sourceRegion, destRegion);
1419 destinationOffset.setLocation(destRegion.getLocation());
1420
1421 // At this point the header has been read and we know
1422 // how many bands are in the image, so perform checking
1423 // of the read param.
1424 int colorType = metadata.IHDR_colorType;
1425 checkReadParamBandSettings(param,
1426 inputBandsForColorType[colorType],
1427 theImage.getSampleModel().getNumBands());
1428
1429 clearAbortRequest();
1430 processImageStarted(0);
1431 if (abortRequested()) {
1432 processReadAborted();
1433 } else {
1434 decodeImage();
1435 if (abortRequested()) {
1436 processReadAborted();
1437 } else {
1438 processImageComplete();
1439 }
1440 }
1441
1442 } catch (IOException e) {
1443 throw new IIOException("Error reading PNG image data", e);
1444 } finally {
1445 if (inf != null) {
1446 inf.end();
1447 }
1489 readHeader();
1490
1491 ArrayList<ImageTypeSpecifier> l =
1492 new ArrayList<ImageTypeSpecifier>(1);
1493
1494 ColorSpace rgb;
1495 ColorSpace gray;
1496 int[] bandOffsets;
1497
1498 int bitDepth = metadata.IHDR_bitDepth;
1499 int colorType = metadata.IHDR_colorType;
1500
1501 int dataType;
1502 if (bitDepth <= 8) {
1503 dataType = DataBuffer.TYPE_BYTE;
1504 } else {
1505 dataType = DataBuffer.TYPE_USHORT;
1506 }
1507
1508 switch (colorType) {
1509 case PNG_COLOR_GRAY:
1510 // Packed grayscale
1511 l.add(ImageTypeSpecifier.createGrayscale(bitDepth,
1512 dataType,
1513 false));
1514 break;
1515
1516 case PNG_COLOR_RGB:
1517 if (bitDepth == 8) {
1518 // some standard types of buffered images
1519 // which can be used as destination
1520 l.add(ImageTypeSpecifier.createFromBufferedImageType(
1521 BufferedImage.TYPE_3BYTE_BGR));
1522
1523 l.add(ImageTypeSpecifier.createFromBufferedImageType(
1524 BufferedImage.TYPE_INT_RGB));
1525
1526 l.add(ImageTypeSpecifier.createFromBufferedImageType(
1527 BufferedImage.TYPE_INT_BGR));
1528
1529 }
1530 // Component R, G, B
1531 rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
1532 bandOffsets = new int[3];
1533 bandOffsets[0] = 0;
1534 bandOffsets[1] = 1;
1535 bandOffsets[2] = 2;
1536 l.add(ImageTypeSpecifier.createInterleaved(rgb,
1537 bandOffsets,
1538 dataType,
1539 false,
1540 false));
1541 break;
1542
1543 case PNG_COLOR_PALETTE:
1544 readMetadata(); // Need tRNS chunk
1545
1546 /*
1547 * The PLTE chunk spec says:
1548 *
|
681 stream.readFully(b);
682 metadata.zTXt_text.add(new String(inflate(b), "ISO-8859-1"));
683
684 // Check if the text chunk contains image creation time
685 if (keyword.equals(PNGMetadata.tEXt_creationTimeKey)) {
686 // Update Standard/Document/ImageCreationTime from text chunk
687 int index = metadata.zTXt_text.size() - 1;
688 metadata.decodeImageCreationTimeFromTextChunk(
689 metadata.zTXt_text.listIterator(index));
690 }
691 }
692
693 private void readMetadata() throws IIOException {
694 if (gotMetadata) {
695 return;
696 }
697
698 readHeader();
699
700 /*
701 * Optimization: We can skip reading metadata if ignoreMetadata
702 * flag is set and colorType is not PNG_COLOR_PALETTE. However,
703 * we parse tRNS chunk to retrieve the transparent color from the
704 * metadata. Doing so, helps PNGImageReader to appropriately
705 * identify and set transparent pixels in the decoded image for
706 * colorType PNG_COLOR_RGB and PNG_COLOR_GRAY.
707 */
708 int colorType = metadata.IHDR_colorType;
709 if (ignoreMetadata && colorType != PNG_COLOR_PALETTE) {
710 try {
711 while (true) {
712 int chunkLength = stream.readInt();
713
714 // verify the chunk length first
715 if (chunkLength < 0 || chunkLength + 4 < 0) {
716 throw new IIOException("Invalid chunk length " + chunkLength);
717 }
718
719 int chunkType = stream.readInt();
720
721 if (chunkType == IDAT_TYPE) {
722 // We've reached the first IDAT chunk position
723 stream.skipBytes(-8);
724 imageStartPosition = stream.getStreamPosition();
725 /*
726 * According to PNG specification tRNS chunk must
727 * precede the first IDAT chunk. So we can stop
728 * reading metadata.
729 */
730 break;
731 } else if (chunkType == tRNS_TYPE) {
732 parse_tRNS_chunk(chunkLength);
733 // After parsing tRNS chunk we will skip 4 CRC bytes
734 stream.skipBytes(4);
735 } else {
736 // Skip the chunk plus the 4 CRC bytes that follow
737 stream.skipBytes(chunkLength + 4);
738 }
739 }
740 } catch (IOException e) {
741 throw new IIOException("Error skipping PNG metadata", e);
742 }
743
744 gotMetadata = true;
745 return;
746 }
747
748 try {
749 loop: while (true) {
750 int chunkLength = stream.readInt();
751 int chunkType = stream.readInt();
752 int chunkCRC;
753
754 // verify the chunk length
1260 idx += 2;
1261 }
1262 }
1263
1264 // True Y position in source
1265 int sourceY = srcY*yStep + yStart;
1266 if ((sourceY >= sourceRegion.y) &&
1267 (sourceY < sourceRegion.y + sourceRegion.height) &&
1268 (((sourceY - sourceRegion.y) %
1269 sourceYSubsampling) == 0)) {
1270
1271 int dstY = destinationOffset.y +
1272 (sourceY - sourceRegion.y)/sourceYSubsampling;
1273 if (dstY < dstMinY) {
1274 continue;
1275 }
1276 if (dstY > dstMaxY) {
1277 break;
1278 }
1279
1280 /*
1281 * In case of colortype PNG_COLOR_RGB or PNG_COLOR_GRAY
1282 * if we have transparent pixel information from tRNS chunk
1283 * we need to consider that also and store proper information
1284 * in alpha channel.
1285 *
1286 * Also we create destination image with extra alpha channel
1287 * in getImageTypes() when we have tRNS chunk for colorType
1288 * PNG_COLOR_RGB or PNG_COLOR_GRAY.
1289 */
1290 boolean tRNSTransparentPixelPresent =
1291 theImage.getSampleModel().getNumBands() == inputBands + 1 &&
1292 metadata.tRNS_present &&
1293 (metadata.tRNS_colorType == PNG_COLOR_RGB ||
1294 metadata.tRNS_colorType == PNG_COLOR_GRAY);
1295 if (useSetRect &&
1296 !tRNSTransparentPixelPresent)
1297 {
1298 imRas.setRect(updateMinX, dstY, passRow);
1299 } else {
1300 int newSrcX = srcX;
1301
1302 for (int dstX = updateMinX;
1303 dstX < updateMinX + updateWidth;
1304 dstX += updateXStep) {
1305
1306 passRow.getPixel(newSrcX, 0, ps);
1307 if (adjustBitDepths) {
1308 for (int b = 0; b < numBands; b++) {
1309 ps[b] = scale[b][ps[b]];
1310 }
1311 }
1312 if (tRNSTransparentPixelPresent)
1313 {
1314 /*
1315 * Create intermediate array to fill the extra
1316 * alpha channel for each pixel.
1317 */
1318 int[] temp = new int[inputBands + 1];
1319 if (metadata.tRNS_colorType == PNG_COLOR_RGB) {
1320 temp[0] = ps[0];
1321 temp[1] = ps[1];
1322 temp[2] = ps[2];
1323 if (ps[0] == metadata.tRNS_red &&
1324 ps[1] == metadata.tRNS_green &&
1325 ps[2] == metadata.tRNS_blue)
1326 {
1327 temp[3] = 0;
1328 } else {
1329 if (bitDepth < 16) {
1330 temp[3] = 255;
1331 } else {
1332 temp[3] = 65535;
1333 }
1334 }
1335 } else {
1336 // when tRNS_colorType is PNG_COLOR_GRAY
1337 temp[0] = ps[0];
1338 if (ps[0] == metadata.tRNS_gray)
1339 {
1340 temp[1] = 0;
1341 } else {
1342 if (bitDepth < 16) {
1343 temp[1] = 255;
1344 } else {
1345 temp[1] = 65535;
1346 }
1347 }
1348 }
1349 imRas.setPixel(dstX, dstY, temp);
1350 } else {
1351 imRas.setPixel(dstX, dstY, ps);
1352 }
1353 newSrcX += srcXStep;
1354 }
1355 }
1356
1357 processImageUpdate(theImage,
1358 updateMinX, dstY,
1359 updateWidth, 1,
1360 updateXStep, updateYStep,
1361 destinationBands);
1362 }
1363 }
1364
1365 processPassComplete(theImage);
1366 }
1367
1368 private void decodeImage()
1369 throws IOException, IIOException {
1370 int width = metadata.IHDR_width;
1371 int height = metadata.IHDR_height;
1372
1473 * the level of application, so we will not try to estimate
1474 * the required amount of the memory and/or handle OOM in
1475 * any way.
1476 */
1477 theImage = getDestination(param,
1478 getImageTypes(0),
1479 width,
1480 height);
1481
1482 Rectangle destRegion = new Rectangle(0, 0, 0, 0);
1483 sourceRegion = new Rectangle(0, 0, 0, 0);
1484 computeRegions(param, width, height,
1485 theImage,
1486 sourceRegion, destRegion);
1487 destinationOffset.setLocation(destRegion.getLocation());
1488
1489 // At this point the header has been read and we know
1490 // how many bands are in the image, so perform checking
1491 // of the read param.
1492 int colorType = metadata.IHDR_colorType;
1493 if (theImage.getSampleModel().getNumBands() ==
1494 inputBandsForColorType[colorType] + 1 &&
1495 metadata.tRNS_present &&
1496 (metadata.tRNS_colorType == PNG_COLOR_RGB ||
1497 metadata.tRNS_colorType == PNG_COLOR_GRAY))
1498 {
1499 checkReadParamBandSettings(param,
1500 inputBandsForColorType[colorType] + 1,
1501 theImage.getSampleModel().getNumBands());
1502 } else {
1503 checkReadParamBandSettings(param,
1504 inputBandsForColorType[colorType],
1505 theImage.getSampleModel().getNumBands());
1506 }
1507
1508 clearAbortRequest();
1509 processImageStarted(0);
1510 if (abortRequested()) {
1511 processReadAborted();
1512 } else {
1513 decodeImage();
1514 if (abortRequested()) {
1515 processReadAborted();
1516 } else {
1517 processImageComplete();
1518 }
1519 }
1520
1521 } catch (IOException e) {
1522 throw new IIOException("Error reading PNG image data", e);
1523 } finally {
1524 if (inf != null) {
1525 inf.end();
1526 }
1568 readHeader();
1569
1570 ArrayList<ImageTypeSpecifier> l =
1571 new ArrayList<ImageTypeSpecifier>(1);
1572
1573 ColorSpace rgb;
1574 ColorSpace gray;
1575 int[] bandOffsets;
1576
1577 int bitDepth = metadata.IHDR_bitDepth;
1578 int colorType = metadata.IHDR_colorType;
1579
1580 int dataType;
1581 if (bitDepth <= 8) {
1582 dataType = DataBuffer.TYPE_BYTE;
1583 } else {
1584 dataType = DataBuffer.TYPE_USHORT;
1585 }
1586
1587 switch (colorType) {
1588 /*
1589 * In case of PNG_COLOR_RGB or PNG_COLOR_GRAY, if we
1590 * have transparent pixel information in tRNS chunk
1591 * we create destination image having alpha channel.
1592 */
1593 case PNG_COLOR_GRAY:
1594 readMetadata(); // Need tRNS chunk
1595
1596 if (metadata.tRNS_present &&
1597 metadata.tRNS_colorType == PNG_COLOR_GRAY)
1598 {
1599 gray = ColorSpace.getInstance(ColorSpace.CS_GRAY);
1600 bandOffsets = new int[2];
1601 bandOffsets[0] = 0;
1602 bandOffsets[1] = 1;
1603 l.add(ImageTypeSpecifier.createInterleaved(gray,
1604 bandOffsets,
1605 dataType,
1606 true,
1607 false));
1608 }
1609 // Packed grayscale
1610 l.add(ImageTypeSpecifier.createGrayscale(bitDepth,
1611 dataType,
1612 false));
1613 break;
1614
1615 case PNG_COLOR_RGB:
1616 readMetadata(); // Need tRNS chunk
1617
1618 if (bitDepth == 8) {
1619 if (metadata.tRNS_present &&
1620 metadata.tRNS_colorType == PNG_COLOR_RGB)
1621 {
1622 l.add(ImageTypeSpecifier.createFromBufferedImageType(
1623 BufferedImage.TYPE_4BYTE_ABGR));
1624 }
1625 // some standard types of buffered images
1626 // which can be used as destination
1627 l.add(ImageTypeSpecifier.createFromBufferedImageType(
1628 BufferedImage.TYPE_3BYTE_BGR));
1629
1630 l.add(ImageTypeSpecifier.createFromBufferedImageType(
1631 BufferedImage.TYPE_INT_RGB));
1632
1633 l.add(ImageTypeSpecifier.createFromBufferedImageType(
1634 BufferedImage.TYPE_INT_BGR));
1635
1636 }
1637 if (metadata.tRNS_present &&
1638 metadata.tRNS_colorType == PNG_COLOR_RGB)
1639 {
1640 rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
1641 bandOffsets = new int[4];
1642 bandOffsets[0] = 0;
1643 bandOffsets[1] = 1;
1644 bandOffsets[2] = 2;
1645 bandOffsets[3] = 3;
1646
1647 l.add(ImageTypeSpecifier.
1648 createInterleaved(rgb, bandOffsets,
1649 dataType, true, false));
1650 }
1651 // Component R, G, B
1652 rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
1653 bandOffsets = new int[3];
1654 bandOffsets[0] = 0;
1655 bandOffsets[1] = 1;
1656 bandOffsets[2] = 2;
1657 l.add(ImageTypeSpecifier.createInterleaved(rgb,
1658 bandOffsets,
1659 dataType,
1660 false,
1661 false));
1662 break;
1663
1664 case PNG_COLOR_PALETTE:
1665 readMetadata(); // Need tRNS chunk
1666
1667 /*
1668 * The PLTE chunk spec says:
1669 *
|