001/*
002 * $RCSfile: PaletteBox.java,v $
003 *
004 * 
005 * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
006 * 
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions
009 * are met: 
010 * 
011 * - Redistribution of source code must retain the above copyright 
012 *   notice, this  list of conditions and the following disclaimer.
013 * 
014 * - Redistribution in binary form must reproduce the above copyright
015 *   notice, this list of conditions and the following disclaimer in 
016 *   the documentation and/or other materials provided with the
017 *   distribution.
018 * 
019 * Neither the name of Sun Microsystems, Inc. or the names of 
020 * contributors may be used to endorse or promote products derived 
021 * from this software without specific prior written permission.
022 * 
023 * This software is provided "AS IS," without a warranty of any 
024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035 * POSSIBILITY OF SUCH DAMAGES. 
036 * 
037 * You acknowledge that this software is not designed or intended for 
038 * use in the design, construction, operation or maintenance of any 
039 * nuclear facility. 
040 *
041 * $Revision: 1.1 $
042 * $Date: 2005/02/11 05:01:36 $
043 * $State: Exp $
044 */
045package com.github.jaiimageio.jpeg2000.impl;
046
047import java.awt.image.IndexColorModel;
048
049import javax.imageio.metadata.IIOInvalidTreeException;
050import javax.imageio.metadata.IIOMetadataNode;
051
052import org.w3c.dom.Node;
053import org.w3c.dom.NodeList;
054
055import com.github.jaiimageio.impl.common.ImageUtil;
056
057/** This class is designed to represent a palette box for JPEG 2000 JP2 file
058 *  format.  A palette box has a length, and a fixed type of "pclr".
059 *
060 * Its content contains the number of palette entry, the number of color
061 * components, the bit depths of the output components, the LUT.
062 *
063 * Currently, only 8-bit color index is supported.
064 */
065public class PaletteBox extends Box {
066    /** The value of the data elements.
067     */
068    private int numEntries;
069    private int numComps;
070    private byte[] bitDepth;
071    private byte[][] lut;
072
073    /** Compute the length of this box. */
074    private static int computeLength(IndexColorModel icm) {
075        int size = icm.getMapSize();
076        int[] comp = icm.getComponentSize();
077        return 11 + comp.length + size * comp.length;
078    }
079
080    /** Gets the size of the components or the bit depth for all the color
081     *  coomponents.
082     */
083    private static byte[] getCompSize(IndexColorModel icm) {
084        int[] comp = icm.getComponentSize();
085        int size = comp.length;
086        byte[] buf = new byte[size];
087        for (int i = 0; i < size; i++)
088            buf[i] = (byte)(comp[i] - 1);
089        return buf;
090    }
091
092    /** Gets the LUT from the <code>IndexColorModel</code> as an two-dimensional
093     *  byte array.
094     */
095    private static byte[][] getLUT(IndexColorModel icm) {
096        int[] comp = icm.getComponentSize();
097        int size = icm.getMapSize();
098        byte[][] lut = new byte[comp.length][size];
099        icm.getReds(lut[0]);
100        icm.getGreens(lut[1]);
101        icm.getBlues(lut[2]);
102        if (comp.length == 4)
103            icm.getAlphas(lut[3]);
104        return lut;
105    }
106
107    /** Constructs a <code>PlatteBox</code> from an
108     *  <code>IndexColorModel</code>.
109     */
110    public PaletteBox(IndexColorModel icm) {
111        this(computeLength(icm), getCompSize(icm), getLUT(icm));
112    }
113
114    /** Constructs a <code>PlatteBox</code> from an
115     *  <code>org.w3c.dom.Node</code>.
116     */
117    public PaletteBox(Node node) throws IIOInvalidTreeException {
118        super(node);
119        byte[][] tlut = null;
120        int index = 0;
121
122        NodeList children = node.getChildNodes();
123        for (int i = 0; i < children.getLength(); i++) {
124            Node child = children.item(i);
125            String name = child.getNodeName();
126
127            if ("NumberEntries".equals(name)) {
128                numEntries = Box.getIntElementValue(child);
129            }
130
131            if ("NumberColors".equals(name)) {
132                numComps = Box.getIntElementValue(child);
133            }
134
135            if ("BitDepth".equals(name)) {
136                bitDepth = Box.getByteArrayElementValue(child);
137            }
138
139            if ("LUT".equals(name)) {
140                tlut = new byte[numEntries][];
141
142                NodeList children1 = child.getChildNodes();
143
144                for (int j = 0; j <children1.getLength(); j++) {
145                    Node child1 = children1.item(j);
146                    name = child1.getNodeName();
147                    if ("LUTRow".equals(name)) {
148                        tlut[index++] = Box.getByteArrayElementValue(child1);
149                    }
150                }
151            }
152        }
153
154        //XXX: currently only 8-bit LUT is supported so no decode is needed
155        // For more refer to read palette box section.
156        lut = new byte[numComps][numEntries];
157
158        for (int i = 0; i < numComps; i++)
159            for (int j = 0; j < numEntries; j++)
160                lut[i][j] = tlut[j][i];
161
162    }
163
164    /** Constructs a <code>PlatteBox</code> from the provided length, bit
165     *  depths of the color components and the LUT.
166     */
167    public PaletteBox(int length, byte[] comp, byte[][] lut) {
168        super(length, 0x70636C72, null);
169        this.bitDepth = comp;
170        this.lut = lut;
171        this.numEntries = lut[0].length;
172        this.numComps = lut.length;
173    }
174
175    /** Constructs a <code>PlatteBox</code> from the provided byte array.
176     */
177    public PaletteBox(byte[] data) {
178        super(8 + data.length, 0x70636C72, data);
179    }
180
181    /** Return the number of palette entries. */
182    public int getNumEntries() {
183        return numEntries;
184    }
185
186    /** Return the number of color components. */
187    public int getNumComp() {
188        return numComps;
189    }
190
191    /** Return the bit depths for all the color components. */
192    public byte[] getBitDepths() {
193        return bitDepth;
194    }
195
196    /** Return the LUT. */
197    public byte[][] getLUT() {
198        return lut;
199    }
200
201    /** creates an <code>IIOMetadataNode</code> from this palette box.
202     *  The format of this node is defined in the XML dtd and xsd
203     *  for the JP2 image file.
204     */
205    public IIOMetadataNode getNativeNode() {
206        IIOMetadataNode node = new IIOMetadataNode(Box.getName(getType()));
207        setDefaultAttributes(node);
208
209        IIOMetadataNode child = new IIOMetadataNode("NumberEntries");
210        child.setUserObject(new Integer(numEntries));
211        child.setNodeValue("" + numEntries);
212        node.appendChild(child);
213
214        child = new IIOMetadataNode("NumberColors");
215        child.setUserObject(new Integer(numComps));
216        child.setNodeValue("" + numComps);
217        node.appendChild(child);
218
219        child = new IIOMetadataNode("BitDepth");
220        child.setUserObject(bitDepth);
221        child.setNodeValue(ImageUtil.convertObjectToString(bitDepth));
222        node.appendChild(child);
223
224        child = new IIOMetadataNode("LUT");
225        for (int i = 0; i < numEntries; i++) {
226            IIOMetadataNode child1 = new IIOMetadataNode("LUTRow");
227            byte[] row = new byte[numComps];
228            for (int j = 0; j < numComps; j++)
229                row[j] = lut[j][i];
230
231            child1.setUserObject(row);
232            child1.setNodeValue(ImageUtil.convertObjectToString(row));
233            child.appendChild(child1);
234        }
235        node.appendChild(child);
236
237        return node;
238    }
239
240    protected void parse(byte[] data) {
241        if (data == null)
242            return;
243        numEntries = (short)(((data[0] & 0xFF) << 8) | (data[1] & 0xFF));
244
245        numComps = data[2];
246        bitDepth = new byte[numComps];
247        System.arraycopy(data, 3, bitDepth, 0, numComps);
248
249        lut = new byte[numComps][numEntries];
250        for (int i = 0, k = 3 + numComps; i < numEntries; i++)
251            for (int j = 0; j < numComps; j++)
252                lut[j][i] = data[k++];
253    }
254
255    protected void compose() {
256        if (data != null)
257            return;
258        data = new byte[3 + numComps + numEntries * numComps];
259        data[0] = (byte)(numEntries >> 8);
260        data[1] = (byte)(numEntries & 0xFF);
261
262        data[2] = (byte)numComps;
263        System.arraycopy(bitDepth, 0, data, 3, numComps);
264
265        for (int i = 0, k = 3 + numComps; i < numEntries; i++)
266            for (int j = 0; j < numComps; j++)
267                data[k++] = lut[j][i];
268    }
269}