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