| /* |
| * 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(); |
| */ |
| } |
| } |
| } |