001/*
002 * $RCSfile: FileFormatWriter.java,v $
003 * $Revision: 1.2 $
004 * $Date: 2006/08/21 22:58:22 $
005 * $State: Exp $
006 *
007 * Class:                   FileFormatWriter
008 *
009 * Description:             Writes the file format
010 *
011 *
012 *
013 * COPYRIGHT:
014 *
015 * This software module was originally developed by Raphaël Grosbois and
016 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
017 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
018 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
019 * Centre France S.A) in the course of development of the JPEG2000
020 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
021 * software module is an implementation of a part of the JPEG 2000
022 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
023 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
024 * Partners) agree not to assert against ISO/IEC and users of the JPEG
025 * 2000 Standard (Users) any of their rights under the copyright, not
026 * including other intellectual property rights, for this software module
027 * with respect to the usage by ISO/IEC and Users of this software module
028 * or modifications thereof for use in hardware or software products
029 * claiming conformance to the JPEG 2000 Standard. Those intending to use
030 * this software module in hardware or software products are advised that
031 * their use may infringe existing patents. The original developers of
032 * this software module, JJ2000 Partners and ISO/IEC assume no liability
033 * for use of this software module or modifications thereof. No license
034 * or right to this software module is granted for non JPEG 2000 Standard
035 * conforming products. JJ2000 Partners have full right to use this
036 * software module for his/her own purpose, assign or donate this
037 * software module to any third party and to inhibit third parties from
038 * using this software module for non JPEG 2000 Standard conforming
039 * products. This copyright notice must be included in all copies or
040 * derivative works of this software module.
041 *
042 * Copyright (c) 1999/2000 JJ2000 Partners.
043 *  */
044package jj2000.j2k.fileformat.writer;
045
046import java.awt.image.ColorModel;
047import java.awt.image.DataBuffer;
048import java.awt.image.IndexColorModel;
049import java.awt.image.SampleModel;
050
051import jj2000.j2k.codestream.*;
052import jj2000.j2k.fileformat.*;
053import jj2000.j2k.io.*;
054
055import java.io.*;
056
057import javax.imageio.stream.ImageOutputStream;
058import javax.imageio.metadata.IIOMetadataNode;
059import org.w3c.dom.NodeList;
060import com.sun.media.imageioimpl.plugins.jpeg2000.Box;
061import com.sun.media.imageioimpl.plugins.jpeg2000.J2KMetadata;
062import com.sun.media.imageioimpl.plugins.jpeg2000.J2KMetadataFormat;
063
064/**
065 * This class writes the file format wrapper that may or may not exist around
066 * a valid JPEG 2000 codestream. This class writes the simple possible legal
067 * fileformat
068 *
069 * @see jj2000.j2k.fileformat.reader.FileFormatReader
070 * */
071public class FileFormatWriter implements FileFormatBoxes {
072
073    /** The name of the file from which to read the codestream and to write
074     * the JP2 file*/
075    private File file;
076
077    private ImageOutputStream stream;
078
079    /** Image height */
080    private int height;
081
082    /** Image width */
083    private int width;
084
085    /** Number of components */
086    private int nc;
087
088    /** Bits per component */
089    private int bpc[];
090
091    /** Flag indicating whether number of bits per component varies */
092    private boolean bpcVaries;
093
094    /** Length of codestream */
095    private int clength;
096
097    /** Length of Colour Specification Box */
098    private static final int CSB_LENGTH = 15;
099
100    /** Length of File Type Box */
101    private static final int FTB_LENGTH = 20;
102
103    /** Length of Image Header Box */
104    private static final int IHB_LENGTH = 22;
105
106    /** base length of Bits Per Component box */
107    private static final int BPC_LENGTH = 8;
108
109    /** The color model of the image to be compressed. */
110    private ColorModel colorModel;
111
112    /** The sample model of the image to be compressed. */
113    private SampleModel sampleModel;
114
115    /** The sample model of the image to be compressed. */
116    private J2KMetadata metadata;
117
118    /** Indicates that the <code>colorModel</code> is a
119     *  <code>IndexColorModel</code>
120     */
121    private boolean isIndexed = false;
122
123    /** The length not counted by the orginal JJ2000 packages. */
124    private int otherLength;
125
126    /** cache the <code>J2KMetadataFormat</code> */
127    J2KMetadataFormat format ;
128
129    /**
130     * The constructor of the FileFormatWriter. It receives all the
131     * information necessary about a codestream to generate a legal JP2 file
132     *
133     * @param filename The name of the file that is to be made a JP2 file
134     *
135     * @param height The height of the image
136     *
137     * @param width The width of the image
138     *
139     * @param nc The number of components
140     *
141     * @param bpc The number of bits per component
142     *
143     * @param clength Length of codestream
144     * @param colorModel The color model of the image to be compressed.
145     */
146    public FileFormatWriter(File file, ImageOutputStream stream,
147                            int height, int width, int nc,
148                            int[] bpc, int clength,
149                            ColorModel colorModel,
150                            SampleModel sampleModel,
151                            J2KMetadata metadata){
152        this.height = height;
153        this.width = width;
154        this.nc = nc;
155        this.bpc = bpc;
156        this.file=file;
157        this.stream = stream;
158        this.clength = clength;
159        this.colorModel = colorModel;
160        this.sampleModel = sampleModel;
161        this.metadata = metadata;
162
163        if (colorModel instanceof IndexColorModel)
164            isIndexed = true;
165
166        bpcVaries=false;
167        int fixbpc = bpc[0];
168        for(int i=nc-1; i>0 ; i--) {
169            if(bpc[i] != fixbpc)
170                bpcVaries = true;
171        }
172    }
173
174
175
176    /**
177     * This method reads the codestream and writes the file format wrapper and
178     * the codestream to the same file
179     *
180     * @return The number of bytes increases because of the file format
181     *
182     * @exception java.io.IOException If an I/O error ocurred.
183     * */
184    public int writeFileFormat() throws IOException {
185        writeMetadata(metadata);
186
187        // Write the Codestream box
188        writeContiguousCodeStreamBox();
189
190        return CSB_LENGTH + otherLength;
191    }
192
193    private void writeMetadata(J2KMetadata metadata) throws IOException {
194        if (metadata == null)
195            return;
196
197        IIOMetadataNode root =
198            (IIOMetadataNode)metadata.getAsTree("com_sun_media_imageio_plugins_jpeg2000_image_1.0");
199        if (root == null)
200            return;
201        format = (J2KMetadataFormat)metadata.getMetadataFormat("com_sun_media_imageio_plugins_jpeg2000_image_1.0");
202        writeSuperBox(root);
203    }
204
205    private void writeSuperBox(IIOMetadataNode node) throws IOException {
206        NodeList list = node.getChildNodes();
207
208        String name = node.getNodeName();
209        if (name.startsWith("JPEG2000")) {
210            stream.writeInt(computeLength(node));
211            stream.writeInt(Box.getTypeInt((String)Box.getTypeByName(name)));
212            otherLength += 8;
213        }
214
215        for (int i = 0; i < list.getLength(); i++) {
216            IIOMetadataNode child = (IIOMetadataNode)list.item(i);
217
218            name = child.getNodeName();
219            if (name.startsWith("JPEG2000") && format.isLeaf(name))
220                writeBox(child);
221            else
222                writeSuperBox(child);
223        }
224    }
225
226    private void writeBox(IIOMetadataNode node) throws IOException {
227        int type = Box.getTypeInt((String)Box.getAttribute(node, "Type"));
228        int length = new Integer((String)Box.getAttribute(node, "Length")).intValue();
229        Box box = Box.createBox(type, node);
230        otherLength += length;
231        stream.writeInt(length);
232        stream.writeInt(type);
233        byte[] data = box.getContent();
234        stream.write(data, 0, data.length);
235    }
236
237    private int computeLength(IIOMetadataNode root) {
238        NodeList list = root.getChildNodes();
239        int length = 0;
240        for (int i = 0; i < list.getLength(); i++) {
241            IIOMetadataNode node = (IIOMetadataNode)list.item(i);
242            String name = node.getNodeName();
243
244            if (format.isLeaf(name))
245                length += new Integer((String)Box.getAttribute(node, "Length")).intValue();
246            else
247                length += computeLength(node);
248
249        }
250
251        return length + (root.getNodeName().startsWith("JPEG2000") ? 8 : 0) ;
252    }
253
254    /**
255     * This method writes the Contiguous codestream box
256     *
257     * @param cs The contiguous codestream
258     *
259     * @exception java.io.IOException If an I/O error ocurred.
260     * */
261    public void writeContiguousCodeStreamBox()throws IOException {
262
263        //when write a jp2 file
264        if (metadata != null) {
265            // Write box length (LBox)
266            // This value is set to 0 since in this implementation, this box is
267            // always last
268            stream.writeInt(clength+8);
269
270            // Write contiguous codestream box name (TBox)
271            stream.writeInt(CONTIGUOUS_CODESTREAM_BOX);
272        }
273            // Read and buffer the codestream
274        BEBufferedRandomAccessFile fi =
275            new BEBufferedRandomAccessFile(file,"rw+");
276        int remainder = clength;
277        byte[] codestream = new byte[1024];
278
279        while(remainder >0) {
280            int len = remainder > 1024 ? 1024 : remainder;
281            fi.readFully(codestream, 0, len);
282
283            // Write codestream
284            stream.write(codestream, 0, len);
285            remainder -= len;
286        }
287
288        // Close the file.
289        fi.close();
290    }
291}