90
91 /** An intermediate Raster holding compressor-friendly data */
92 private WritableRaster raster = null;
93
94 /**
95 * Set to true if we are writing an image with an
96 * indexed ColorModel
97 */
98 private boolean indexed = false;
99 private IndexColorModel indexCM = null;
100
101 private boolean convertTosRGB = false; // Used by PhotoYCC only
102 private WritableRaster converted = null;
103
104 private boolean isAlphaPremultiplied = false;
105 private ColorModel srcCM = null;
106
107 /**
108 * If there are thumbnails to be written, this is the list.
109 */
110 private List thumbnails = null;
111
112 /**
113 * If metadata should include an icc profile, store it here.
114 */
115 private ICC_Profile iccProfile = null;
116
117 private int sourceXOffset = 0;
118 private int sourceYOffset = 0;
119 private int sourceWidth = 0;
120 private int [] srcBands = null;
121 private int sourceHeight = 0;
122
123 /** Used when calling listeners */
124 private int currentImage = 0;
125
126 private ColorConvertOp convertOp = null;
127
128 private JPEGQTable [] streamQTables = null;
129 private JPEGHuffmanTable[] streamDCHuffmanTables = null;
130 private JPEGHuffmanTable[] streamACHuffmanTables = null;
1377 warningOccurred(input
1378 ? WARNING_IMAGE_METADATA_ADOBE_MISMATCH
1379 : WARNING_DEST_METADATA_ADOBE_MISMATCH);
1380 if (rightTransform == JPEG.ADOBE_IMPOSSIBLE) {
1381 ignoreAdobe = true;
1382 } else {
1383 newAdobeTransform = rightTransform;
1384 }
1385 }
1386 }
1387 }
1388
1389 /**
1390 * Collect all the scan info from the given metadata, and
1391 * organize it into the scan info array required by the
1392 * IJG libray. It is much simpler to parse out this
1393 * data in Java and then just copy the data in C.
1394 */
1395 private int [] collectScans(JPEGMetadata metadata,
1396 SOFMarkerSegment sof) {
1397 List segments = new ArrayList();
1398 int SCAN_SIZE = 9;
1399 int MAX_COMPS_PER_SCAN = 4;
1400 for (Iterator iter = metadata.markerSequence.iterator();
1401 iter.hasNext();) {
1402 MarkerSegment seg = (MarkerSegment) iter.next();
1403 if (seg instanceof SOSMarkerSegment) {
1404 segments.add(seg);
1405 }
1406 }
1407 int [] retval = null;
1408 numScans = 0;
1409 if (!segments.isEmpty()) {
1410 numScans = segments.size();
1411 retval = new int [numScans*SCAN_SIZE];
1412 int index = 0;
1413 for (int i = 0; i < numScans; i++) {
1414 SOSMarkerSegment sos = (SOSMarkerSegment) segments.get(i);
1415 retval[index++] = sos.componentSpecs.length; // num comps
1416 for (int j = 0; j < MAX_COMPS_PER_SCAN; j++) {
1417 if (j < sos.componentSpecs.length) {
1418 int compSel = sos.componentSpecs[j].componentSelector;
1419 for (int k = 0; k < sof.componentSpecs.length; k++) {
1420 if (compSel == sof.componentSpecs[k].componentId) {
1421 retval[index++] = k;
1422 break; // out of for over sof comps
1423 }
1424 }
1425 } else {
1426 retval[index++] = 0;
1427 }
1428 }
1429 retval[index++] = sos.startSpectralSelection;
1430 retval[index++] = sos.endSpectralSelection;
1431 retval[index++] = sos.approxHigh;
1432 retval[index++] = sos.approxLow;
1433 }
1434 }
1435 return retval;
1436 }
1437
1438 /**
1439 * Finds all DQT marker segments and returns all the q
1440 * tables as a single array of JPEGQTables.
1441 */
1442 private JPEGQTable [] collectQTablesFromMetadata
1443 (JPEGMetadata metadata) {
1444 ArrayList tables = new ArrayList();
1445 Iterator iter = metadata.markerSequence.iterator();
1446 while (iter.hasNext()) {
1447 MarkerSegment seg = (MarkerSegment) iter.next();
1448 if (seg instanceof DQTMarkerSegment) {
1449 DQTMarkerSegment dqt =
1450 (DQTMarkerSegment) seg;
1451 tables.addAll(dqt.tables);
1452 }
1453 }
1454 JPEGQTable [] retval = null;
1455 if (tables.size() != 0) {
1456 retval = new JPEGQTable[tables.size()];
1457 for (int i = 0; i < retval.length; i++) {
1458 retval[i] =
1459 new JPEGQTable(((DQTMarkerSegment.Qtable)tables.get(i)).data);
1460 }
1461 }
1462 return retval;
1463 }
1464
1465 /**
1466 * Finds all DHT marker segments and returns all the q
1467 * tables as a single array of JPEGQTables. The metadata
1468 * must not be for a progressive image, or an exception
1469 * will be thrown when two Huffman tables with the same
1470 * table id are encountered.
1471 */
1472 private JPEGHuffmanTable[] collectHTablesFromMetadata
1473 (JPEGMetadata metadata, boolean wantDC) throws IIOException {
1474 ArrayList tables = new ArrayList();
1475 Iterator iter = metadata.markerSequence.iterator();
1476 while (iter.hasNext()) {
1477 MarkerSegment seg = (MarkerSegment) iter.next();
1478 if (seg instanceof DHTMarkerSegment) {
1479 DHTMarkerSegment dht =
1480 (DHTMarkerSegment) seg;
1481 for (int i = 0; i < dht.tables.size(); i++) {
1482 DHTMarkerSegment.Htable htable =
1483 (DHTMarkerSegment.Htable) dht.tables.get(i);
1484 if (htable.tableClass == (wantDC ? 0 : 1)) {
1485 tables.add(htable);
1486 }
1487 }
1488 }
1489 }
1490 JPEGHuffmanTable [] retval = null;
1491 if (tables.size() != 0) {
1492 DHTMarkerSegment.Htable [] htables =
1493 new DHTMarkerSegment.Htable[tables.size()];
1494 tables.toArray(htables);
1495 retval = new JPEGHuffmanTable[tables.size()];
1496 for (int i = 0; i < retval.length; i++) {
1497 retval[i] = null;
1498 for (int j = 0; j < tables.size(); j++) {
1499 if (htables[j].tableID == i) {
1500 if (retval[i] != null) {
1501 throw new IIOException("Metadata has duplicate Htables!");
1502 }
1503 retval[i] = new JPEGHuffmanTable(htables[j].numCodes,
1536 retval = JPEG.JCS_RGBA;
1537 } else {
1538 retval = JPEG.JCS_RGB;
1539 }
1540 break;
1541 case ColorSpace.TYPE_YCbCr:
1542 if (alpha) {
1543 retval = JPEG.JCS_YCbCrA;
1544 } else {
1545 retval = JPEG.JCS_YCbCr;
1546 }
1547 break;
1548 case ColorSpace.TYPE_3CLR:
1549 if (cs == JPEG.JCS.getYCC()) {
1550 if (alpha) {
1551 retval = JPEG.JCS_YCCA;
1552 } else {
1553 retval = JPEG.JCS_YCC;
1554 }
1555 }
1556 case ColorSpace.TYPE_CMYK:
1557 retval = JPEG.JCS_CMYK;
1558 break;
1559 }
1560 }
1561 return retval;
1562 }
1563
1564 private int getDestCSType(ImageTypeSpecifier destType) {
1565 ColorModel cm = destType.getColorModel();
1566 boolean alpha = cm.hasAlpha();
1567 ColorSpace cs = cm.getColorSpace();
1568 int retval = JPEG.JCS_UNKNOWN;
1569 switch (cs.getType()) {
1570 case ColorSpace.TYPE_GRAY:
1571 retval = JPEG.JCS_GRAYSCALE;
1572 break;
1573 case ColorSpace.TYPE_RGB:
1574 if (alpha) {
1575 retval = JPEG.JCS_RGBA;
1576 } else {
1577 retval = JPEG.JCS_RGB;
1578 }
1579 break;
1580 case ColorSpace.TYPE_YCbCr:
1581 if (alpha) {
1582 retval = JPEG.JCS_YCbCrA;
1583 } else {
1584 retval = JPEG.JCS_YCbCr;
1585 }
1586 break;
1587 case ColorSpace.TYPE_3CLR:
1588 if (cs == JPEG.JCS.getYCC()) {
1589 if (alpha) {
1590 retval = JPEG.JCS_YCCA;
1591 } else {
1592 retval = JPEG.JCS_YCC;
1593 }
1594 }
1595 case ColorSpace.TYPE_CMYK:
1596 retval = JPEG.JCS_CMYK;
1597 break;
1598 }
1599 return retval;
1600 }
1601
1602 private int getDefaultDestCSType(ImageTypeSpecifier type) {
1603 return getDefaultDestCSType(type.getColorModel());
1604 }
1605
1606 private int getDefaultDestCSType(RenderedImage rimage) {
1607 return getDefaultDestCSType(rimage.getColorModel());
1608 }
1609
1610 private int getDefaultDestCSType(ColorModel cm) {
1611 int retval = JPEG.JCS_UNKNOWN;
1612 if (cm != null) {
1613 boolean alpha = cm.hasAlpha();
1614 ColorSpace cs = cm.getColorSpace();
1621 retval = JPEG.JCS_YCbCrA;
1622 } else {
1623 retval = JPEG.JCS_YCbCr;
1624 }
1625 break;
1626 case ColorSpace.TYPE_YCbCr:
1627 if (alpha) {
1628 retval = JPEG.JCS_YCbCrA;
1629 } else {
1630 retval = JPEG.JCS_YCbCr;
1631 }
1632 break;
1633 case ColorSpace.TYPE_3CLR:
1634 if (cs == JPEG.JCS.getYCC()) {
1635 if (alpha) {
1636 retval = JPEG.JCS_YCCA;
1637 } else {
1638 retval = JPEG.JCS_YCC;
1639 }
1640 }
1641 case ColorSpace.TYPE_CMYK:
1642 retval = JPEG.JCS_YCCK;
1643 break;
1644 }
1645 }
1646 return retval;
1647 }
1648
1649 private boolean isSubsampled(SOFMarkerSegment.ComponentSpec [] specs) {
1650 int hsamp0 = specs[0].HsamplingFactor;
1651 int vsamp0 = specs[0].VsamplingFactor;
1652 for (int i = 1; i < specs.length; i++) {
1653 if ((specs[i].HsamplingFactor != hsamp0) ||
1654 (specs[i].HsamplingFactor != hsamp0))
1655 return true;
1656 }
1657 return false;
1658 }
1659
1660 ////////////// End of ColorSpace conversion
1661
1662 ////////////// Native methods and callbacks
1663
1664 /** Sets up static native structures. */
1665 private static native void initWriterIDs(Class qTableClass,
1666 Class huffClass);
1667
1668 /** Sets up per-writer native structure and returns a pointer to it. */
1669 private native long initJPEGImageWriter();
1670
1671 /** Sets up native structures for output stream */
1672 private native void setDest(long structPointer);
1673
1674 /**
1675 * Returns <code>true</code> if the write was aborted.
1676 */
1677 private native boolean writeImage(long structPointer,
1678 byte [] data,
1679 int inCsType, int outCsType,
1680 int numBands,
1681 int [] bandSizes,
1682 int srcWidth,
1683 int destWidth, int destHeight,
1684 int stepX, int stepY,
1685 JPEGQTable [] qtables,
1686 boolean writeDQT,
|
90
91 /** An intermediate Raster holding compressor-friendly data */
92 private WritableRaster raster = null;
93
94 /**
95 * Set to true if we are writing an image with an
96 * indexed ColorModel
97 */
98 private boolean indexed = false;
99 private IndexColorModel indexCM = null;
100
101 private boolean convertTosRGB = false; // Used by PhotoYCC only
102 private WritableRaster converted = null;
103
104 private boolean isAlphaPremultiplied = false;
105 private ColorModel srcCM = null;
106
107 /**
108 * If there are thumbnails to be written, this is the list.
109 */
110 private List<? extends BufferedImage> thumbnails = null;
111
112 /**
113 * If metadata should include an icc profile, store it here.
114 */
115 private ICC_Profile iccProfile = null;
116
117 private int sourceXOffset = 0;
118 private int sourceYOffset = 0;
119 private int sourceWidth = 0;
120 private int [] srcBands = null;
121 private int sourceHeight = 0;
122
123 /** Used when calling listeners */
124 private int currentImage = 0;
125
126 private ColorConvertOp convertOp = null;
127
128 private JPEGQTable [] streamQTables = null;
129 private JPEGHuffmanTable[] streamDCHuffmanTables = null;
130 private JPEGHuffmanTable[] streamACHuffmanTables = null;
1377 warningOccurred(input
1378 ? WARNING_IMAGE_METADATA_ADOBE_MISMATCH
1379 : WARNING_DEST_METADATA_ADOBE_MISMATCH);
1380 if (rightTransform == JPEG.ADOBE_IMPOSSIBLE) {
1381 ignoreAdobe = true;
1382 } else {
1383 newAdobeTransform = rightTransform;
1384 }
1385 }
1386 }
1387 }
1388
1389 /**
1390 * Collect all the scan info from the given metadata, and
1391 * organize it into the scan info array required by the
1392 * IJG libray. It is much simpler to parse out this
1393 * data in Java and then just copy the data in C.
1394 */
1395 private int [] collectScans(JPEGMetadata metadata,
1396 SOFMarkerSegment sof) {
1397 List<SOSMarkerSegment> segments = new ArrayList<>();
1398 int SCAN_SIZE = 9;
1399 int MAX_COMPS_PER_SCAN = 4;
1400 for (Iterator<MarkerSegment> iter = metadata.markerSequence.iterator();
1401 iter.hasNext();) {
1402 MarkerSegment seg = iter.next();
1403 if (seg instanceof SOSMarkerSegment) {
1404 segments.add((SOSMarkerSegment) seg);
1405 }
1406 }
1407 int [] retval = null;
1408 numScans = 0;
1409 if (!segments.isEmpty()) {
1410 numScans = segments.size();
1411 retval = new int [numScans*SCAN_SIZE];
1412 int index = 0;
1413 for (int i = 0; i < numScans; i++) {
1414 SOSMarkerSegment sos = segments.get(i);
1415 retval[index++] = sos.componentSpecs.length; // num comps
1416 for (int j = 0; j < MAX_COMPS_PER_SCAN; j++) {
1417 if (j < sos.componentSpecs.length) {
1418 int compSel = sos.componentSpecs[j].componentSelector;
1419 for (int k = 0; k < sof.componentSpecs.length; k++) {
1420 if (compSel == sof.componentSpecs[k].componentId) {
1421 retval[index++] = k;
1422 break; // out of for over sof comps
1423 }
1424 }
1425 } else {
1426 retval[index++] = 0;
1427 }
1428 }
1429 retval[index++] = sos.startSpectralSelection;
1430 retval[index++] = sos.endSpectralSelection;
1431 retval[index++] = sos.approxHigh;
1432 retval[index++] = sos.approxLow;
1433 }
1434 }
1435 return retval;
1436 }
1437
1438 /**
1439 * Finds all DQT marker segments and returns all the q
1440 * tables as a single array of JPEGQTables.
1441 */
1442 private JPEGQTable [] collectQTablesFromMetadata
1443 (JPEGMetadata metadata) {
1444 ArrayList<DQTMarkerSegment.Qtable> tables = new ArrayList<>();
1445 Iterator<MarkerSegment> iter = metadata.markerSequence.iterator();
1446 while (iter.hasNext()) {
1447 MarkerSegment seg = iter.next();
1448 if (seg instanceof DQTMarkerSegment) {
1449 DQTMarkerSegment dqt =
1450 (DQTMarkerSegment) seg;
1451 tables.addAll(dqt.tables);
1452 }
1453 }
1454 JPEGQTable [] retval = null;
1455 if (tables.size() != 0) {
1456 retval = new JPEGQTable[tables.size()];
1457 for (int i = 0; i < retval.length; i++) {
1458 retval[i] =
1459 new JPEGQTable(tables.get(i).data);
1460 }
1461 }
1462 return retval;
1463 }
1464
1465 /**
1466 * Finds all DHT marker segments and returns all the q
1467 * tables as a single array of JPEGQTables. The metadata
1468 * must not be for a progressive image, or an exception
1469 * will be thrown when two Huffman tables with the same
1470 * table id are encountered.
1471 */
1472 private JPEGHuffmanTable[] collectHTablesFromMetadata
1473 (JPEGMetadata metadata, boolean wantDC) throws IIOException {
1474 ArrayList<DHTMarkerSegment.Htable> tables = new ArrayList<>();
1475 Iterator<MarkerSegment> iter = metadata.markerSequence.iterator();
1476 while (iter.hasNext()) {
1477 MarkerSegment seg = iter.next();
1478 if (seg instanceof DHTMarkerSegment) {
1479 DHTMarkerSegment dht = (DHTMarkerSegment) seg;
1480 for (int i = 0; i < dht.tables.size(); i++) {
1481 DHTMarkerSegment.Htable htable = dht.tables.get(i);
1482 if (htable.tableClass == (wantDC ? 0 : 1)) {
1483 tables.add(htable);
1484 }
1485 }
1486 }
1487 }
1488 JPEGHuffmanTable [] retval = null;
1489 if (tables.size() != 0) {
1490 DHTMarkerSegment.Htable [] htables =
1491 new DHTMarkerSegment.Htable[tables.size()];
1492 tables.toArray(htables);
1493 retval = new JPEGHuffmanTable[tables.size()];
1494 for (int i = 0; i < retval.length; i++) {
1495 retval[i] = null;
1496 for (int j = 0; j < tables.size(); j++) {
1497 if (htables[j].tableID == i) {
1498 if (retval[i] != null) {
1499 throw new IIOException("Metadata has duplicate Htables!");
1500 }
1501 retval[i] = new JPEGHuffmanTable(htables[j].numCodes,
1534 retval = JPEG.JCS_RGBA;
1535 } else {
1536 retval = JPEG.JCS_RGB;
1537 }
1538 break;
1539 case ColorSpace.TYPE_YCbCr:
1540 if (alpha) {
1541 retval = JPEG.JCS_YCbCrA;
1542 } else {
1543 retval = JPEG.JCS_YCbCr;
1544 }
1545 break;
1546 case ColorSpace.TYPE_3CLR:
1547 if (cs == JPEG.JCS.getYCC()) {
1548 if (alpha) {
1549 retval = JPEG.JCS_YCCA;
1550 } else {
1551 retval = JPEG.JCS_YCC;
1552 }
1553 }
1554 break;
1555 case ColorSpace.TYPE_CMYK:
1556 retval = JPEG.JCS_CMYK;
1557 break;
1558 }
1559 }
1560 return retval;
1561 }
1562
1563 private int getDestCSType(ImageTypeSpecifier destType) {
1564 ColorModel cm = destType.getColorModel();
1565 boolean alpha = cm.hasAlpha();
1566 ColorSpace cs = cm.getColorSpace();
1567 int retval = JPEG.JCS_UNKNOWN;
1568 switch (cs.getType()) {
1569 case ColorSpace.TYPE_GRAY:
1570 retval = JPEG.JCS_GRAYSCALE;
1571 break;
1572 case ColorSpace.TYPE_RGB:
1573 if (alpha) {
1574 retval = JPEG.JCS_RGBA;
1575 } else {
1576 retval = JPEG.JCS_RGB;
1577 }
1578 break;
1579 case ColorSpace.TYPE_YCbCr:
1580 if (alpha) {
1581 retval = JPEG.JCS_YCbCrA;
1582 } else {
1583 retval = JPEG.JCS_YCbCr;
1584 }
1585 break;
1586 case ColorSpace.TYPE_3CLR:
1587 if (cs == JPEG.JCS.getYCC()) {
1588 if (alpha) {
1589 retval = JPEG.JCS_YCCA;
1590 } else {
1591 retval = JPEG.JCS_YCC;
1592 }
1593 }
1594 break;
1595 case ColorSpace.TYPE_CMYK:
1596 retval = JPEG.JCS_CMYK;
1597 break;
1598 }
1599 return retval;
1600 }
1601
1602 private int getDefaultDestCSType(ImageTypeSpecifier type) {
1603 return getDefaultDestCSType(type.getColorModel());
1604 }
1605
1606 private int getDefaultDestCSType(RenderedImage rimage) {
1607 return getDefaultDestCSType(rimage.getColorModel());
1608 }
1609
1610 private int getDefaultDestCSType(ColorModel cm) {
1611 int retval = JPEG.JCS_UNKNOWN;
1612 if (cm != null) {
1613 boolean alpha = cm.hasAlpha();
1614 ColorSpace cs = cm.getColorSpace();
1621 retval = JPEG.JCS_YCbCrA;
1622 } else {
1623 retval = JPEG.JCS_YCbCr;
1624 }
1625 break;
1626 case ColorSpace.TYPE_YCbCr:
1627 if (alpha) {
1628 retval = JPEG.JCS_YCbCrA;
1629 } else {
1630 retval = JPEG.JCS_YCbCr;
1631 }
1632 break;
1633 case ColorSpace.TYPE_3CLR:
1634 if (cs == JPEG.JCS.getYCC()) {
1635 if (alpha) {
1636 retval = JPEG.JCS_YCCA;
1637 } else {
1638 retval = JPEG.JCS_YCC;
1639 }
1640 }
1641 break;
1642 case ColorSpace.TYPE_CMYK:
1643 retval = JPEG.JCS_YCCK;
1644 break;
1645 }
1646 }
1647 return retval;
1648 }
1649
1650 private boolean isSubsampled(SOFMarkerSegment.ComponentSpec [] specs) {
1651 int hsamp0 = specs[0].HsamplingFactor;
1652 int vsamp0 = specs[0].VsamplingFactor;
1653 for (int i = 1; i < specs.length; i++) {
1654 if ((specs[i].HsamplingFactor != hsamp0) ||
1655 (specs[i].HsamplingFactor != hsamp0))
1656 return true;
1657 }
1658 return false;
1659 }
1660
1661 ////////////// End of ColorSpace conversion
1662
1663 ////////////// Native methods and callbacks
1664
1665 /** Sets up static native structures. */
1666 private static native void initWriterIDs(Class<?> qTableClass,
1667 Class<?> huffClass);
1668
1669 /** Sets up per-writer native structure and returns a pointer to it. */
1670 private native long initJPEGImageWriter();
1671
1672 /** Sets up native structures for output stream */
1673 private native void setDest(long structPointer);
1674
1675 /**
1676 * Returns <code>true</code> if the write was aborted.
1677 */
1678 private native boolean writeImage(long structPointer,
1679 byte [] data,
1680 int inCsType, int outCsType,
1681 int numBands,
1682 int [] bandSizes,
1683 int srcWidth,
1684 int destWidth, int destHeight,
1685 int stepX, int stepY,
1686 JPEGQTable [] qtables,
1687 boolean writeDQT,
|