001/*
002 * $RCSfile: FileFormatReader.java,v $
003 * $Revision: 1.2 $
004 * $Date: 2005/04/28 01:25:38 $
005 * $State: Exp $
006 *
007 * Class:                   FileFormatReader
008 *
009 * Description:             Read J2K file stream
010 *
011 * COPYRIGHT:
012 *
013 * This software module was originally developed by Raphaël Grosbois and
014 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
015 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
016 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
017 * Centre France S.A) in the course of development of the JPEG2000
018 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
019 * software module is an implementation of a part of the JPEG 2000
020 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
021 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
022 * Partners) agree not to assert against ISO/IEC and users of the JPEG
023 * 2000 Standard (Users) any of their rights under the copyright, not
024 * including other intellectual property rights, for this software module
025 * with respect to the usage by ISO/IEC and Users of this software module
026 * or modifications thereof for use in hardware or software products
027 * claiming conformance to the JPEG 2000 Standard. Those intending to use
028 * this software module in hardware or software products are advised that
029 * their use may infringe existing patents. The original developers of
030 * this software module, JJ2000 Partners and ISO/IEC assume no liability
031 * for use of this software module or modifications thereof. No license
032 * or right to this software module is granted for non JPEG 2000 Standard
033 * conforming products. JJ2000 Partners have full right to use this
034 * software module for his/her own purpose, assign or donate this
035 * software module to any third party and to inhibit third parties from
036 * using this software module for non JPEG 2000 Standard conforming
037 * products. This copyright notice must be included in all copies or
038 * derivative works of this software module.
039 *
040 * Copyright (c) 1999/2000 JJ2000 Partners.
041 * */
042package jj2000.j2k.fileformat.reader;
043
044import java.awt.Transparency;
045import java.awt.color.ICC_Profile;
046import java.awt.color.ColorSpace;
047import java.awt.color.ICC_ColorSpace;
048import java.awt.image.ColorModel;
049import java.awt.image.ComponentColorModel;
050import java.awt.image.DataBuffer;
051import java.awt.image.IndexColorModel;
052
053import jj2000.j2k.codestream.*;
054import jj2000.j2k.fileformat.*;
055import jj2000.j2k.io.*;
056
057import java.util.*;
058import java.io.*;
059import com.sun.media.imageioimpl.plugins.jpeg2000.Box;
060import com.sun.media.imageioimpl.plugins.jpeg2000.BitsPerComponentBox;
061import com.sun.media.imageioimpl.plugins.jpeg2000.ChannelDefinitionBox;
062import com.sun.media.imageioimpl.plugins.jpeg2000.ColorSpecificationBox;
063import com.sun.media.imageioimpl.plugins.jpeg2000.ComponentMappingBox;
064import com.sun.media.imageioimpl.plugins.jpeg2000.DataEntryURLBox;
065import com.sun.media.imageioimpl.plugins.jpeg2000.FileTypeBox;
066import com.sun.media.imageioimpl.plugins.jpeg2000.HeaderBox;
067import com.sun.media.imageioimpl.plugins.jpeg2000.J2KMetadata;
068import com.sun.media.imageioimpl.plugins.jpeg2000.ResolutionBox;
069import com.sun.media.imageioimpl.plugins.jpeg2000.PaletteBox;
070import com.sun.media.imageioimpl.plugins.jpeg2000.UUIDBox;
071import com.sun.media.imageioimpl.plugins.jpeg2000.UUIDListBox;
072import com.sun.media.imageioimpl.plugins.jpeg2000.XMLBox;
073import com.sun.media.imageioimpl.plugins.jpeg2000.SignatureBox;
074
075/**
076 * This class reads the file format wrapper that may or may not exist around a
077 * valid JPEG 2000 codestream. Since no information from the file format is
078 * used in the actual decoding, this class simply goes through the file and
079 * finds the first valid codestream.
080 *
081 * @see jj2000.j2k.fileformat.writer.FileFormatWriter
082 * */
083public class FileFormatReader implements FileFormatBoxes{
084
085    /** The random access from which the file format boxes are read */
086    private RandomAccessIO in;
087
088    /** The positions of the codestreams in the fileformat*/
089    private Vector codeStreamPos;
090
091    /** The lengths of the codestreams in the fileformat*/
092    private Vector codeStreamLength;
093
094    /** Create a IndexColorModel from the palette box if there is one */
095    private ColorModel colorModel = null;
096
097    /** The meta data */
098    private J2KMetadata metadata;
099
100    /** Parameters in header box */
101    private int width;
102    private int height;
103    private int numComp;
104    private int bitDepth;
105    private int compressionType;
106    private int unknownColor;
107    private int intelProp;
108
109    /** Various bit depth */
110    private byte[] bitDepths;
111
112    /** The lut in the palette box */
113    private byte[][] lut;
114    private byte[] compSize;
115
116    /** Component mapping */
117    private short[] comps;
118    private byte[] type;
119    private byte[] maps;
120
121    /** Channel definitions */
122    private short[] channels ;
123    private short[] cType;
124    private short[] associations;
125
126    /** Color specification */
127    private int colorSpaceType;
128
129    /** ICC profile */
130    private ICC_Profile profile;
131
132    /**
133     * The constructor of the FileFormatReader
134     *
135     * @param in The RandomAccessIO from which to read the file format
136     * */
137    public FileFormatReader(RandomAccessIO in, J2KMetadata metadata){
138        this.in = in;
139        this.metadata = metadata;
140    }
141
142
143    /**
144     * This method checks whether the given RandomAccessIO is a valid JP2 file
145     * and if so finds the first codestream in the file. Currently, the
146     * information in the codestream is not used
147     *
148     * @param in The RandomAccessIO from which to read the file format
149     *
150     * @exception java.io.IOException If an I/O error ocurred.
151     *
152     * @exception java.io.EOFException If end of file is reached
153     * */
154    public void readFileFormat() throws IOException, EOFException {
155
156        int foundCodeStreamBoxes=0;
157        int box;
158        int length;
159        long longLength=0;
160        int pos;
161        short marker;
162        boolean jp2HeaderBoxFound=false;
163        boolean lastBoxFound = false;
164
165        try{
166
167            // Go through the randomaccessio and find the first
168            // contiguous codestream box. Check also that the File Format is
169            // correct
170
171            pos = in.getPos();
172
173            // Make sure that the first 12 bytes is the JP2_SIGNATURE_BOX
174            // or if not that the first 2 bytes is the SOC marker
175            if(in.readInt() != 0x0000000c ||
176               in.readInt() != JP2_SIGNATURE_BOX ||
177               in.readInt() != 0x0d0a870a){ // Not a JP2 file
178                in.seek(pos);
179
180                marker = (short)in.readShort();
181                if(marker != Markers.SOC) //Standard syntax marker found
182                    throw new Error("File is neither valid JP2 file nor "+
183                                    "valid JPEG 2000 codestream");
184                in.seek(pos);
185                if(codeStreamPos == null)
186                    codeStreamPos = new Vector();
187                codeStreamPos.addElement(new Integer(pos));
188                return;
189            }
190
191            if (metadata != null)
192                metadata.addNode(new SignatureBox());
193
194            // Read all remaining boxes
195            while(!lastBoxFound){
196                pos = in.getPos();
197                length = in.readInt();
198                if((pos+length) == in.length())
199                    lastBoxFound = true;
200
201                box = in.readInt();
202                if (length == 0) {
203                    lastBoxFound = true;
204                    length = in.length()-in.getPos();
205                } else if(length == 1) {
206                    longLength = in.readLong();
207                    throw new IOException("File too long.");
208                } else longLength = (long) 0;
209
210                pos = in.getPos();
211                length -= 8;
212
213                switch(box){
214                case FILE_TYPE_BOX:
215                    readFileTypeBox(length + 8, longLength);
216                    break;
217                case CONTIGUOUS_CODESTREAM_BOX:
218                    if(!jp2HeaderBoxFound)
219                        throw new Error("Invalid JP2 file: JP2Header box not "+
220                                        "found before Contiguous codestream "+
221                                        "box ");
222                    readContiguousCodeStreamBox(length + 8, longLength);
223                    break;
224                case JP2_HEADER_BOX:
225                    if(jp2HeaderBoxFound)
226                        throw new Error("Invalid JP2 file: Multiple "+
227                                        "JP2Header boxes found");
228                    readJP2HeaderBox(length + 8);
229                    jp2HeaderBoxFound = true;
230                    length = 0;
231                    break;
232                case IMAGE_HEADER_BOX:
233                    readImageHeaderBox(length);
234                    break;
235                case INTELLECTUAL_PROPERTY_BOX:
236                    readIntPropertyBox(length);
237                    break;
238                case XML_BOX:
239                    readXMLBox(length);
240                    break;
241                case UUID_INFO_BOX:
242                    length = 0;
243                    break;
244                case UUID_BOX:
245                    readUUIDBox(length);
246                    break;
247                case UUID_LIST_BOX:
248                    readUUIDListBox(length);
249                    break;
250                case URL_BOX:
251                    readURLBox(length);
252                    break;
253                case PALETTE_BOX:
254                    readPaletteBox(length + 8);
255                    break;
256                case BITS_PER_COMPONENT_BOX:
257                    readBitsPerComponentBox(length);
258                    break;
259                case COMPONENT_MAPPING_BOX:
260                    readComponentMappingBox(length);
261                    break;
262                case COLOUR_SPECIFICATION_BOX:
263                    readColourSpecificationBox(length);
264                    break;
265                case CHANNEL_DEFINITION_BOX:
266                    readChannelDefinitionBox(length);
267                    break;
268                case RESOLUTION_BOX:
269                    length = 0;
270                    break;
271                case CAPTURE_RESOLUTION_BOX:
272                case DEFAULT_DISPLAY_RESOLUTION_BOX:
273                    readResolutionBox(box, length);
274                    break;
275                default:
276                    if (metadata != null) {
277                        byte[] data = new byte[length];
278                        in.readFully(data, 0, length);
279                        metadata.addNode(new Box(length + 8,
280                                                 box,
281                                                 longLength,
282                                                 data));
283                    }
284                }
285                if(!lastBoxFound)
286                    in.seek(pos+length);
287            }
288        }catch( EOFException e ){
289            throw new Error("EOF reached before finding Contiguous "+
290                            "Codestream Box");
291        }
292
293        if(codeStreamPos.size() == 0){
294          // Not a valid JP2 file or codestream
295          throw new Error("Invalid JP2 file: Contiguous codestream box "+
296                          "missing");
297        }
298
299        return;
300    }
301
302    /**
303     * This method reads the File Type box
304     *
305     * @return false if the File Type box was not found or invalid else true
306     *
307     * @exception java.io.IOException If an I/O error ocurred.
308     *
309     * @exception java.io.EOFException If the end of file was reached
310     * */
311    public boolean readFileTypeBox(int length, long longLength)
312        throws IOException, EOFException {
313        int nComp;
314        boolean foundComp=false;
315
316        // Check for XLBox
317        if(length == 1) { // Box has 8 byte length;
318            longLength = in.readLong();
319            throw new IOException("File too long.");
320        }
321
322        // Check that this is a correct DBox value
323        // Read Brand field
324        if(in.readInt() != FT_BR)
325            return false;
326
327        // Read MinV field
328        int minorVersion = in.readInt();
329
330        // Check that there is at least one FT_BR entry in in
331        // compatibility list
332        nComp = (length - 16)/4; // Number of compatibilities.
333        int[] comp = new int[nComp];
334        for(int i=0; i < nComp; i++){
335            if((comp[i] = in.readInt()) == FT_BR)
336                foundComp = true;
337        }
338        if(!foundComp)
339            return false;
340
341        if (metadata != null)
342            metadata.addNode(new FileTypeBox(FT_BR, minorVersion, comp));
343
344        return true;
345    }
346
347    /**
348     * This method reads the JP2Header box
349     *
350     * @param pos The position in the file
351     *
352     * @param length The length of the JP2Header box
353     *
354     * @param long length The length of the JP2Header box if greater than
355     * 1<<32
356     *
357     * @return false if the JP2Header box was not found or invalid else true
358     *
359     * @exception java.io.IOException If an I/O error ocurred.
360     *
361     * @exception java.io.EOFException If the end of file was reached
362     * */
363    public boolean readJP2HeaderBox(int length)
364        throws IOException, EOFException {
365
366        if(length == 0) // This can not be last box
367            throw new Error("Zero-length of JP2Header Box");
368
369        // Here the JP2Header data (DBox) would be read if we were to use it
370        return true;
371    }
372
373   /**
374     * This method reads the Image Header box
375     * @param length The length of the JP2Header box
376     *
377     * @return false if the JP2Header box was not found or invalid else true
378     *
379     * @exception java.io.IOException If an I/O error ocurred.
380     *
381     * @exception java.io.EOFException If the end of file was reached
382     * */
383    public boolean readImageHeaderBox(int length)
384        throws IOException, EOFException {
385
386        if(length == 0) // This can not be last box
387            throw new Error("Zero-length of JP2Header Box");
388
389        // Here the JP2Header data (DBox) would be read if we were to use it
390
391        height = in.readInt();
392        width = in.readInt();
393        numComp = in.readShort();
394        bitDepth = in.readByte();
395
396        compressionType = in.readByte();
397        unknownColor = in.readByte();
398        intelProp = in.readByte();
399
400        if (metadata != null) {
401
402            metadata.addNode(new HeaderBox(height, width, numComp, bitDepth,
403                                           compressionType, unknownColor,
404                                           intelProp));
405        }
406        return true;
407    }
408
409     /**
410     * This method skips the Contiguous codestream box and adds position
411     * of contiguous codestream to a vector
412     *
413     * @param pos The position in the file
414     *
415     * @param length The length of the JP2Header box
416     *
417     * @param long length The length of the JP2Header box if greater than 1<<32
418     *
419     * @return false if the Contiguous codestream box was not found or invalid
420     * else true
421     *
422     * @exception java.io.IOException If an I/O error ocurred.
423     *
424     * @exception java.io.EOFException If the end of file was reached
425     * */
426    public boolean readContiguousCodeStreamBox(int length,
427                                               long longLength)
428        throws IOException, EOFException {
429
430        // Add new codestream position to position vector
431        int ccpos = in.getPos();
432
433        if(codeStreamPos == null)
434            codeStreamPos = new Vector();
435        codeStreamPos.addElement(new Integer(ccpos));
436
437        // Add new codestream length to length vector
438        if(codeStreamLength == null)
439            codeStreamLength = new Vector();
440        codeStreamLength.addElement(new Integer(length));
441
442        return true;
443    }
444
445    /**
446     * This method reads the contents of the Intellectual property box
447     * */
448    public void readIntPropertyBox(int length) throws IOException {
449        if (metadata != null) {
450            byte[] data = new byte[length];
451            in.readFully(data, 0, length);
452            metadata.addNode(new Box(length + 8, 0x6A703269, data));
453        }
454    }
455
456    /**
457     * This method reads the contents of the XML box
458     */
459    public void readXMLBox(int length) throws IOException {
460        if (metadata != null) {
461            byte[] data = new byte[length];
462            in.readFully(data, 0, length);
463            metadata.addNode(new XMLBox(data));
464        }
465    }
466
467    /**
468     * This method reads the contents of the XML box
469     */
470    public void readURLBox(int length) throws IOException {
471        if (metadata != null) {
472            byte[] data = new byte[length];
473            in.readFully(data, 0, length);
474            metadata.addNode(new DataEntryURLBox(data));
475        }
476    }
477
478    /**
479     * This method reads the contents of the Intellectual property box
480     */
481    public void readUUIDBox(int length) throws IOException {
482        if (metadata != null) {
483            byte[] data = new byte[length];
484            in.readFully(data, 0, length);
485            metadata.addNode(new UUIDBox(data));
486        }
487    }
488
489    /**
490     * This method reads the contents of the UUID List box
491     * */
492    public void readUUIDListBox(int length) throws IOException {
493        if (metadata != null) {
494            byte[] data = new byte[length];
495            in.readFully(data, 0, length);
496            metadata.addNode(new UUIDListBox(data));
497        }
498    }
499
500    /** This method reads the content of the palette box */
501    public void readPaletteBox(int length) throws IOException {
502        // Get current position in file
503        int pos = in.getPos();
504
505        int lutSize = in.readShort();
506        int numComp = in.readByte();
507        compSize = new byte[numComp];
508
509        for (int i = 0; i < numComp; i++) {
510            compSize[i] = (byte)in.readByte();
511        }
512
513        lut = new byte[numComp][lutSize];
514
515        for (int n=0; n < lutSize; n++) {
516            for (int c = 0; c < numComp; c++) {
517                int depth = 1 + (compSize[c] & 0x7F);
518                if (depth > 32)
519                    depth = 32;
520                int numBytes = (depth + 7)>>3;
521                int mask = (1 << depth) - 1;
522                byte[] buf = new byte[numBytes];
523                in.readFully(buf, 0, numBytes);
524
525                int val = 0;
526
527                for (int k = 0; k < numBytes; k++) {
528                    val = buf[k] + (val << 8);
529                }
530                lut[c][n] = (byte)val;
531            }
532        }
533        if (metadata != null) {
534            metadata.addNode(new PaletteBox(length, compSize, lut));
535        }
536    }
537
538    /** Read the component mapping channel.
539     */
540    public void readComponentMappingBox(int length)throws IOException {
541        int num = length / 4;
542
543        comps = new short[num];
544        type = new byte[num];
545        maps = new byte[num];
546
547        for (int i = 0; i < num; i++) {
548            comps[i] = in.readShort();
549            type[i] = in.readByte();
550            maps[i] = in.readByte();
551        }
552
553        if (metadata != null) {
554            metadata.addNode(new ComponentMappingBox(comps, type, maps));
555        }
556    }
557
558    /**
559     * This method reads the Channel Definition box
560     *
561     * @exception java.io.IOException If an I/O error ocurred.
562     *
563     */
564    public void readChannelDefinitionBox(int length)throws IOException {
565        int num = in.readShort();
566        channels = new short[num];
567        cType = new short[num];
568        associations = new short[num];
569
570        for (int i = 0; i < num; i++) {
571            channels[i] = in.readShort();
572            cType[i] = in.readShort();
573            associations[i] = in.readShort();
574        }
575        if (metadata != null) {
576            metadata.addNode(new ChannelDefinitionBox(channels, cType, associations));
577        }
578    }
579
580    /** Read the bits per component.
581     */
582    public void readBitsPerComponentBox(int length)throws IOException {
583        bitDepths = new byte[length];
584        in.readFully(bitDepths, 0, length);
585
586        if (metadata != null) {
587            metadata.addNode(new BitsPerComponentBox(bitDepths));
588        }
589    }
590
591    /** Read the color specifications.
592     */
593    public void readColourSpecificationBox(int length)throws IOException {
594        // read METHOD field
595        byte method = (byte)in.readByte();
596
597        // read PREC field
598        byte prec = (byte)in.readByte();
599
600        // read APPROX field
601        byte approx = (byte)in.readByte();
602
603        if (method == 2) {
604            byte[] data = new byte[length - 3];
605            in.readFully(data, 0, data.length);
606            profile = ICC_Profile.getInstance(data);
607        } else  // read EnumCS field
608            colorSpaceType = in.readInt();
609
610        if (metadata != null) {
611            metadata.addNode(new ColorSpecificationBox(method, prec, approx,
612                                                       colorSpaceType,
613                                                       profile));
614        }
615    }
616
617    /** Read the resolution.
618     */
619    public void readResolutionBox(int type, int length)throws IOException {
620        byte[] data = new byte[length];
621        in.readFully(data, 0, length);
622        if (metadata != null) {
623            metadata.addNode(new ResolutionBox(type, data));
624        }
625    }
626
627    /**
628     * This method creates and returns an array of positions to contiguous
629     * codestreams in the file
630     *
631     * @return The positions of the contiguous codestreams in the file
632     * */
633    public long[] getCodeStreamPos(){
634        int size = codeStreamPos.size();
635        long[] pos = new long[size];
636        for(int i=0 ; i<size ; i++)
637            pos[i]=((Integer)(codeStreamPos.elementAt(i))).longValue();
638        return pos;
639    }
640
641    /**
642     * This method returns the position of the first contiguous codestreams in
643     * the file
644     *
645     * @return The position of the first contiguous codestream in the file
646     * */
647    public int getFirstCodeStreamPos(){
648        return ((Integer)(codeStreamPos.elementAt(0))).intValue();
649    }
650
651    /**
652     * This method returns the length of the first contiguous codestreams in
653     * the file
654     *
655     * @return The length of the first contiguous codestream in the file
656     * */
657    public int getFirstCodeStreamLength(){
658        return ((Integer)(codeStreamLength.elementAt(0))).intValue();
659    }
660
661    /**
662     * Returns the color model created from the palette box.
663     */
664    public ColorModel getColorModel() {
665        // Check 'numComp' instance variable here in case there is an
666        // embedded palette such as in the pngsuite images pp0n2c16.png
667        // and pp0n6a08.png.
668        if (lut != null && numComp == 1) {
669            int numComp = lut.length;
670
671            int maxDepth = 1 + (bitDepth & 0x7F);
672
673            if (maps == null) {
674                maps = new byte[numComp];
675                for (int i = 0; i < numComp; i++)
676                    maps[i] = (byte)i;
677            }
678            if (numComp == 3)
679                colorModel = new IndexColorModel(maxDepth, lut[0].length,
680                                                 lut[maps[0]],
681                                                 lut[maps[1]],
682                                                 lut[maps[2]]);
683            else if (numComp == 4)
684                colorModel = new IndexColorModel(maxDepth, lut[0].length,
685                                                 lut[maps[0]],
686                                                 lut[maps[1]],
687                                                 lut[maps[2]],
688                                                 lut[maps[3]]);
689        } else if (channels != null){
690            boolean hasAlpha = false;
691            int alphaChannel = numComp - 1;
692
693            for (int i = 0; i < channels.length; i++) {
694                if (cType[i] == 1 && channels[i] == alphaChannel)
695                    hasAlpha = true;
696            }
697
698            boolean[] isPremultiplied = new boolean[] {false};
699
700            if (hasAlpha) {
701                isPremultiplied = new boolean[alphaChannel];
702
703                for (int i = 0; i < alphaChannel; i++)
704                    isPremultiplied[i] = false;
705
706                for (int i = 0; i < channels.length; i++) {
707                    if (cType[i] == 2)
708                        isPremultiplied[associations[i] - 1] = true;
709                }
710
711                for (int i = 1; i < alphaChannel; i++)
712                    isPremultiplied[0] &= isPremultiplied[i];
713            }
714
715            ColorSpace cs = null;
716            if (profile != null)
717                cs = new ICC_ColorSpace(profile);
718            else if (colorSpaceType == CSB_ENUM_SRGB)
719                cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
720            else if (colorSpaceType == CSB_ENUM_GREY)
721                cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
722            else if (colorSpaceType == CSB_ENUM_YCC)
723                cs = ColorSpace.getInstance(ColorSpace.CS_PYCC);
724
725            int[] bits = new int[numComp];
726            for (int i = 0; i < numComp; i++)
727                if (bitDepths != null)
728                    bits[i] = (bitDepths[i] & 0x7F) + 1;
729                else
730                    bits[i] = (bitDepth &0x7F) + 1;
731
732            int maxBitDepth = 1 + (bitDepth & 0x7F);
733            boolean isSigned = (bitDepth & 0x80) == 0x80;
734            if (bitDepths != null)
735                isSigned = (bitDepths[0]  & 0x80) == 0x80;
736
737            if (bitDepths != null)
738                for (int i = 0; i < numComp; i++)
739                    if (bits[i] > maxBitDepth)
740                        maxBitDepth = bits[i];
741
742            int type = -1;
743
744            if (maxBitDepth <= 8)
745                type = DataBuffer.TYPE_BYTE;
746            else if (maxBitDepth <= 16)
747                type = isSigned ? DataBuffer.TYPE_SHORT : DataBuffer.TYPE_USHORT;
748            else if (maxBitDepth <= 32)
749                type = DataBuffer.TYPE_INT;
750
751            if (type == -1)
752                return null;
753
754            if (cs != null) {
755                colorModel = new ComponentColorModel(cs,
756                                                 bits,
757                                                 hasAlpha,
758                                                 isPremultiplied[0],
759                                                 hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE ,
760                                                 type);
761            }
762        }
763        return colorModel;
764    }
765}