blob: 34fe0d2a8115fa52ded90818c5ba9a891674c35a [file] [log] [blame]
/*
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
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();
*/
}
}
}