You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
310 lines
10 KiB
310 lines
10 KiB
/*
|
|
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package com.sun.imageio.plugins.jpeg;
|
|
|
|
import javax.imageio.IIOException;
|
|
import javax.imageio.metadata.IIOInvalidTreeException;
|
|
import javax.imageio.metadata.IIOMetadataNode;
|
|
import javax.imageio.stream.ImageOutputStream;
|
|
import javax.imageio.plugins.jpeg.JPEGQTable;
|
|
|
|
import java.io.IOException;
|
|
import java.util.List;
|
|
import java.util.ArrayList;
|
|
import java.util.Iterator;
|
|
|
|
import org.w3c.dom.Node;
|
|
import org.w3c.dom.NodeList;
|
|
import org.w3c.dom.NamedNodeMap;
|
|
|
|
/**
|
|
* A DQT (Define Quantization Table) marker segment.
|
|
*/
|
|
class DQTMarkerSegment extends MarkerSegment {
|
|
List tables = new ArrayList(); // Could be 1 to 4
|
|
|
|
DQTMarkerSegment(float quality, boolean needTwo) {
|
|
super(JPEG.DQT);
|
|
tables.add(new Qtable(true, quality));
|
|
if (needTwo) {
|
|
tables.add(new Qtable(false, quality));
|
|
}
|
|
}
|
|
|
|
DQTMarkerSegment(JPEGBuffer buffer) throws IOException {
|
|
super(buffer);
|
|
int count = length;
|
|
while (count > 0) {
|
|
Qtable newGuy = new Qtable(buffer);
|
|
tables.add(newGuy);
|
|
count -= newGuy.data.length+1;
|
|
}
|
|
buffer.bufAvail -= length;
|
|
}
|
|
|
|
DQTMarkerSegment(JPEGQTable[] qtables) {
|
|
super(JPEG.DQT);
|
|
for (int i = 0; i < qtables.length; i++) {
|
|
tables.add(new Qtable(qtables[i], i));
|
|
}
|
|
}
|
|
|
|
DQTMarkerSegment(Node node) throws IIOInvalidTreeException {
|
|
super(JPEG.DQT);
|
|
NodeList children = node.getChildNodes();
|
|
int size = children.getLength();
|
|
if ((size < 1) || (size > 4)) {
|
|
throw new IIOInvalidTreeException("Invalid DQT node", node);
|
|
}
|
|
for (int i = 0; i < size; i++) {
|
|
tables.add(new Qtable(children.item(i)));
|
|
}
|
|
}
|
|
|
|
protected Object clone() {
|
|
DQTMarkerSegment newGuy = (DQTMarkerSegment) super.clone();
|
|
newGuy.tables = new ArrayList(tables.size());
|
|
Iterator iter = tables.iterator();
|
|
while (iter.hasNext()) {
|
|
Qtable table = (Qtable) iter.next();
|
|
newGuy.tables.add(table.clone());
|
|
}
|
|
return newGuy;
|
|
}
|
|
|
|
IIOMetadataNode getNativeNode() {
|
|
IIOMetadataNode node = new IIOMetadataNode("dqt");
|
|
for (int i= 0; i<tables.size(); i++) {
|
|
Qtable table = (Qtable) tables.get(i);
|
|
node.appendChild(table.getNativeNode());
|
|
}
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Writes the data for this segment to the stream in
|
|
* valid JPEG format.
|
|
*/
|
|
void write(ImageOutputStream ios) throws IOException {
|
|
// We don't write DQT segments; the IJG library does.
|
|
}
|
|
|
|
void print() {
|
|
printTag("DQT");
|
|
System.out.println("Num tables: "
|
|
+ Integer.toString(tables.size()));
|
|
for (int i= 0; i<tables.size(); i++) {
|
|
Qtable table = (Qtable) tables.get(i);
|
|
table.print();
|
|
}
|
|
System.out.println();
|
|
}
|
|
|
|
/**
|
|
* Assuming the given table was generated by scaling the "standard"
|
|
* visually lossless luminance table, extract the scale factor that
|
|
* was used.
|
|
*/
|
|
Qtable getChromaForLuma(Qtable luma) {
|
|
Qtable newGuy = null;
|
|
// Determine if the table is all the same values
|
|
// if so, use the same table
|
|
boolean allSame = true;
|
|
for (int i = 1; i < luma.QTABLE_SIZE; i++) {
|
|
if (luma.data[i] != luma.data[i-1]) {
|
|
allSame = false;
|
|
break;
|
|
}
|
|
}
|
|
if (allSame) {
|
|
newGuy = (Qtable) luma.clone();
|
|
newGuy.tableID = 1;
|
|
} else {
|
|
// Otherwise, find the largest coefficient less than 255. This is
|
|
// the largest value that we know did not clamp on scaling.
|
|
int largestPos = 0;
|
|
for (int i = 1; i < luma.QTABLE_SIZE; i++) {
|
|
if (luma.data[i] > luma.data[largestPos]) {
|
|
largestPos = i;
|
|
}
|
|
}
|
|
// Compute the scale factor by dividing it by the value in the
|
|
// same position from the "standard" table.
|
|
// If the given table was not generated by scaling the standard,
|
|
// the resulting table will still be reasonable, as it will reflect
|
|
// a comparable scaling of chrominance frequency response of the
|
|
// eye.
|
|
float scaleFactor = ((float)(luma.data[largestPos]))
|
|
/ ((float)(JPEGQTable.K1Div2Luminance.getTable()[largestPos]));
|
|
// generate a new table
|
|
JPEGQTable jpegTable =
|
|
JPEGQTable.K2Div2Chrominance.getScaledInstance(scaleFactor,
|
|
true);
|
|
newGuy = new Qtable(jpegTable, 1);
|
|
}
|
|
return newGuy;
|
|
}
|
|
|
|
Qtable getQtableFromNode(Node node) throws IIOInvalidTreeException {
|
|
return new Qtable(node);
|
|
}
|
|
|
|
/**
|
|
* A quantization table within a DQT marker segment.
|
|
*/
|
|
class Qtable implements Cloneable {
|
|
int elementPrecision;
|
|
int tableID;
|
|
final int QTABLE_SIZE = 64;
|
|
int [] data; // 64 elements, in natural order
|
|
|
|
/**
|
|
* The zigzag-order position of the i'th element
|
|
* of a DCT block read in natural order.
|
|
*/
|
|
private final int [] zigzag = {
|
|
0, 1, 5, 6, 14, 15, 27, 28,
|
|
2, 4, 7, 13, 16, 26, 29, 42,
|
|
3, 8, 12, 17, 25, 30, 41, 43,
|
|
9, 11, 18, 24, 31, 40, 44, 53,
|
|
10, 19, 23, 32, 39, 45, 52, 54,
|
|
20, 22, 33, 38, 46, 51, 55, 60,
|
|
21, 34, 37, 47, 50, 56, 59, 61,
|
|
35, 36, 48, 49, 57, 58, 62, 63
|
|
};
|
|
|
|
Qtable(boolean wantLuma, float quality) {
|
|
elementPrecision = 0;
|
|
JPEGQTable base = null;
|
|
if (wantLuma) {
|
|
tableID = 0;
|
|
base = JPEGQTable.K1Div2Luminance;
|
|
} else {
|
|
tableID = 1;
|
|
base = JPEGQTable.K2Div2Chrominance;
|
|
}
|
|
if (quality != JPEG.DEFAULT_QUALITY) {
|
|
quality = JPEG.convertToLinearQuality(quality);
|
|
if (wantLuma) {
|
|
base = JPEGQTable.K1Luminance.getScaledInstance
|
|
(quality, true);
|
|
} else {
|
|
base = JPEGQTable.K2Div2Chrominance.getScaledInstance
|
|
(quality, true);
|
|
}
|
|
}
|
|
data = base.getTable();
|
|
}
|
|
|
|
Qtable(JPEGBuffer buffer) throws IIOException {
|
|
elementPrecision = buffer.buf[buffer.bufPtr] >>> 4;
|
|
tableID = buffer.buf[buffer.bufPtr++] & 0xf;
|
|
if (elementPrecision != 0) {
|
|
// IJG is compiled for 8-bits, so this shouldn't happen
|
|
throw new IIOException ("Unsupported element precision");
|
|
}
|
|
data = new int [QTABLE_SIZE];
|
|
// Read from zig-zag order to natural order
|
|
for (int i = 0; i < QTABLE_SIZE; i++) {
|
|
data[i] = buffer.buf[buffer.bufPtr+zigzag[i]] & 0xff;
|
|
}
|
|
buffer.bufPtr += QTABLE_SIZE;
|
|
}
|
|
|
|
Qtable(JPEGQTable table, int id) {
|
|
elementPrecision = 0;
|
|
tableID = id;
|
|
data = table.getTable();
|
|
}
|
|
|
|
Qtable(Node node) throws IIOInvalidTreeException {
|
|
if (node.getNodeName().equals("dqtable")) {
|
|
NamedNodeMap attrs = node.getAttributes();
|
|
int count = attrs.getLength();
|
|
if ((count < 1) || (count > 2)) {
|
|
throw new IIOInvalidTreeException
|
|
("dqtable node must have 1 or 2 attributes", node);
|
|
}
|
|
elementPrecision = 0;
|
|
tableID = getAttributeValue(node, attrs, "qtableId", 0, 3, true);
|
|
if (node instanceof IIOMetadataNode) {
|
|
IIOMetadataNode ourNode = (IIOMetadataNode) node;
|
|
JPEGQTable table = (JPEGQTable) ourNode.getUserObject();
|
|
if (table == null) {
|
|
throw new IIOInvalidTreeException
|
|
("dqtable node must have user object", node);
|
|
}
|
|
data = table.getTable();
|
|
} else {
|
|
throw new IIOInvalidTreeException
|
|
("dqtable node must have user object", node);
|
|
}
|
|
} else {
|
|
throw new IIOInvalidTreeException
|
|
("Invalid node, expected dqtable", node);
|
|
}
|
|
}
|
|
|
|
protected Object clone() {
|
|
Qtable newGuy = null;
|
|
try {
|
|
newGuy = (Qtable) super.clone();
|
|
} catch (CloneNotSupportedException e) {} // won't happen
|
|
if (data != null) {
|
|
newGuy.data = (int []) data.clone();
|
|
}
|
|
return newGuy;
|
|
}
|
|
|
|
IIOMetadataNode getNativeNode() {
|
|
IIOMetadataNode node = new IIOMetadataNode("dqtable");
|
|
node.setAttribute("elementPrecision",
|
|
Integer.toString(elementPrecision));
|
|
node.setAttribute("qtableId",
|
|
Integer.toString(tableID));
|
|
node.setUserObject(new JPEGQTable(data));
|
|
return node;
|
|
}
|
|
|
|
void print() {
|
|
System.out.println("Table id: " + Integer.toString(tableID));
|
|
System.out.println("Element precision: "
|
|
+ Integer.toString(elementPrecision));
|
|
|
|
(new JPEGQTable(data)).toString();
|
|
/*
|
|
for (int i = 0; i < 64; i++) {
|
|
if (i % 8 == 0) {
|
|
System.out.println();
|
|
}
|
|
System.out.print(" " + Integer.toString(data[i]));
|
|
}
|
|
System.out.println();
|
|
*/
|
|
}
|
|
}
|
|
}
|