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