/*
 * Copyright (c) 2001, 2014, 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.
/** * A DQT (Define Quantization Table) marker segment. */
class DQTMarkerSegment extends MarkerSegment { List<Qtable> 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<Qtable> iter = tables.iterator(); while (iter.hasNext()) { Qtable table = iter.next(); newGuy.tables.add((Qtable) table.clone()); } return newGuy; } IIOMetadataNode getNativeNode() { IIOMetadataNode node = new IIOMetadataNode("dqt"); for (int i= 0; i<tables.size(); i++) { Qtable table = tables.get(i); node.appendChild(table.getNativeNode()); } return node; }
Writes the data for this segment to the stream in valid JPEG format.
/** * 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 = 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.
/** * 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.
/** * 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.
/** * 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 = 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(); */ } } }