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,
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,
1643 }
1644 return retval;
1645 }
1646
1647 private boolean isSubsampled(SOFMarkerSegment.ComponentSpec [] specs) {
1648 int hsamp0 = specs[0].HsamplingFactor;
1649 int vsamp0 = specs[0].VsamplingFactor;
1650 for (int i = 1; i < specs.length; i++) {
1651 if ((specs[i].HsamplingFactor != hsamp0) ||
1652 (specs[i].HsamplingFactor != hsamp0))
1653 return true;
1654 }
1655 return false;
1656 }
1657
1658 ////////////// End of ColorSpace conversion
1659
1660 ////////////// Native methods and callbacks
1661
1662 /** Sets up static native structures. */
1663 private static native void initWriterIDs(Class<?> qTableClass,
1664 Class<?> huffClass);
1665
1666 /** Sets up per-writer native structure and returns a pointer to it. */
1667 private native long initJPEGImageWriter();
1668
1669 /** Sets up native structures for output stream */
1670 private native void setDest(long structPointer);
1671
1672 /**
1673 * Returns <code>true</code> if the write was aborted.
1674 */
1675 private native boolean writeImage(long structPointer,
1676 byte [] data,
1677 int inCsType, int outCsType,
1678 int numBands,
1679 int [] bandSizes,
1680 int srcWidth,
1681 int destWidth, int destHeight,
1682 int stepX, int stepY,
1683 JPEGQTable [] qtables,
1684 boolean writeDQT,
|