309 lines · 11.0 KB · Java
RawDownload
/* * 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();            */        }    }}