001: /*
002: * $RCSfile: TextureLoader.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.12 $
041: * $Date: 2007/04/03 23:48:44 $
042: * $State: Exp $
043: */
044:
045: package com.sun.j3d.utils.image;
046:
047: import javax.media.j3d.*;
048: import java.awt.Image;
049: import java.awt.Component;
050: import java.awt.Transparency;
051: import java.awt.color.ColorSpace;
052: import java.awt.geom.AffineTransform;
053: import java.awt.image.*;
054: import java.io.File;
055: import java.io.IOException;
056: import java.net.URL;
057: import java.lang.reflect.Method;
058: import javax.imageio.ImageIO;
059:
060: /**
061: * This class is used for loading a texture from an Image or BufferedImage.
062: * The Image I/O API is used to load the images. (If the JAI IIO Tools
063: * package is available, a larger set of formats can be loaded, including
064: * TIFF, JPEG2000, and so on.)
065: *
066: * Methods are provided to retrieve the Texture object and the associated
067: * ImageComponent object or a scaled version of the ImageComponent object.
068: *
069: * Default format is RGBA. Other legal formats are: RGBA, RGBA4, RGB5_A1,
070: * RGB, RGB4, RGB5, R3_G3_B2, LUM8_ALPHA8, LUM4_ALPHA4, LUMINANCE and ALPHA
071: */
072: public class TextureLoader extends Object {
073:
074: /**
075: * Optional flag - specifies that mipmaps are generated for all levels
076: */
077: public static final int GENERATE_MIPMAP = 0x01;
078:
079: /**
080: * Optional flag - specifies that the ImageComponent2D will
081: * access the image data by reference
082: *
083: * @since Java 3D 1.2
084: */
085: public static final int BY_REFERENCE = 0x02;
086:
087: /**
088: * Optional flag - specifies that the ImageComponent2D will
089: * have a y-orientation of y up, meaning the origin of the image is the
090: * lower left
091: *
092: * @since Java 3D 1.2
093: */
094: public static final int Y_UP = 0x04;
095:
096: /**
097: * Optional flag - specifies that the ImageComponent2D is allowed
098: * to have dimensions that are not a power of two. If this flag is set,
099: * TextureLoader will not perform any scaling of images. If this flag
100: * is not set, images will be scaled to the nearest power of two. This is
101: * the default mode.
102: * <p>
103: * Note that non-power-of-two textures may not be supported by all graphics
104: * cards. Applications should check whether a particular Canvas3D supports
105: * non-power-of-two textures by calling the {@link Canvas3D#queryProperties}
106: * method, and checking whether the
107: * <code>textureNonPowerOfTwoAvailable</code> property is set to true.
108: *
109: * @since Java 3D 1.5
110: */
111: public static final int ALLOW_NON_POWER_OF_TWO = 0x08;
112:
113: /*
114: * Private declaration for BufferedImage allocation
115: */
116: private static ColorSpace cs = ColorSpace
117: .getInstance(ColorSpace.CS_sRGB);
118: private static int[] nBits = { 8, 8, 8, 8 };
119: private static int[] bandOffset = { 0, 1, 2, 3 };
120: private static ComponentColorModel colorModel = new ComponentColorModel(
121: cs, nBits, true, false, Transparency.TRANSLUCENT, 0);
122:
123: private Texture2D tex = null;
124: private BufferedImage bufferedImage = null;
125: private ImageComponent2D imageComponent = null;
126: private int textureFormat = Texture.RGBA;
127: private int imageComponentFormat = ImageComponent.FORMAT_RGBA;
128: private int flags;
129: private boolean byRef = false;
130: private boolean yUp = false;
131: private boolean forcePowerOfTwo = true;
132:
133: /**
134: * Contructs a TextureLoader object using the specified BufferedImage
135: * and default format RGBA
136: * @param bImage The BufferedImage used for loading the texture
137: *
138: * @exception NullPointerException if bImage is null
139: */
140: public TextureLoader(BufferedImage bImage) {
141: this (bImage, null, 0);
142: }
143:
144: /**
145: * Contructs a TextureLoader object using the specified BufferedImage
146: * and format
147: * @param bImage The BufferedImage used for loading the texture
148: * @param format The format specifies which channels to use
149: *
150: * @exception NullPointerException if bImage is null
151: */
152: public TextureLoader(BufferedImage bImage, String format) {
153: this (bImage, format, 0);
154: }
155:
156: /**
157: * Contructs a TextureLoader object using the specified BufferedImage,
158: * option flags and default format RGBA
159: * @param bImage The BufferedImage used for loading the texture
160: * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
161: *
162: * @exception NullPointerException if bImage is null
163: */
164: public TextureLoader(BufferedImage bImage, int flags) {
165: this (bImage, null, flags);
166: }
167:
168: /**
169: * Contructs a TextureLoader object using the specified BufferedImage,
170: * format and option flags
171: * @param bImage The BufferedImage used for loading the texture
172: * @param format The format specifies which channels to use
173: * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
174: *
175: * @exception NullPointerException if bImage is null
176: */
177: public TextureLoader(BufferedImage bImage, String format, int flags) {
178: if (bImage == null) {
179: throw new NullPointerException();
180: }
181:
182: parseFormat(format);
183: this .flags = flags;
184: bufferedImage = bImage;
185: if (format == null)
186: chooseFormat(bufferedImage);
187:
188: if ((flags & BY_REFERENCE) != 0) {
189: byRef = true;
190: }
191: if ((flags & Y_UP) != 0) {
192: yUp = true;
193: }
194: if ((flags & ALLOW_NON_POWER_OF_TWO) != 0) {
195: forcePowerOfTwo = false;
196: }
197: }
198:
199: /**
200: * Contructs a TextureLoader object using the specified Image
201: * and default format RGBA
202: * @param image The Image used for loading the texture
203: * @param observer The associated image observer
204: *
205: * @exception NullPointerException if image is null
206: * @exception ImageException if there is a problem loading the image
207: */
208: public TextureLoader(Image image, Component observer) {
209: this (image, null, 0, observer);
210: }
211:
212: /**
213: * Contructs a TextureLoader object using the specified Image
214: * and format
215: * @param image The Image used for loading the texture
216: * @param format The format specifies which channels to use
217: * @param observer The associated image observer
218: *
219: * @exception NullPointerException if image is null
220: * @exception ImageException if there is a problem loading the image
221: */
222: public TextureLoader(Image image, String format, Component observer) {
223: this (image, format, 0, observer);
224: }
225:
226: /**
227: * Contructs a TextureLoader object using the specified Image
228: * flags and default format RGBA
229: * @param image The Image used for loading the texture
230: * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
231: * @param observer The associated image observer
232: *
233: * @exception NullPointerException if image is null
234: * @exception ImageException if there is a problem loading the image
235: */
236: public TextureLoader(Image image, int flags, Component observer) {
237: this (image, null, flags, observer);
238: }
239:
240: /**
241: * Contructs a TextureLoader object using the specified Image
242: * format and option flags
243: * @param image The Image used for loading the texture
244: * @param format The format specifies which channels to use
245: * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
246: * @param observer The associated image observer
247: *
248: * @exception NullPointerException if image is null
249: * @exception ImageException if there is a problem loading the image
250: */
251: public TextureLoader(Image image, String format, int flags,
252: Component observer) {
253:
254: if (image == null) {
255: throw new NullPointerException();
256: }
257:
258: if (observer == null) {
259: observer = new java.awt.Container();
260: }
261:
262: parseFormat(format);
263: this .flags = flags;
264: bufferedImage = createBufferedImage(image, observer);
265:
266: if (bufferedImage == null) {
267: throw new ImageException("Error loading image: "
268: + image.toString());
269: }
270:
271: if (format == null)
272: chooseFormat(bufferedImage);
273:
274: if ((flags & BY_REFERENCE) != 0) {
275: byRef = true;
276: }
277: if ((flags & Y_UP) != 0) {
278: yUp = true;
279: }
280: if ((flags & ALLOW_NON_POWER_OF_TWO) != 0) {
281: forcePowerOfTwo = false;
282: }
283: }
284:
285: /**
286: * Contructs a TextureLoader object using the specified file
287: * and default format RGBA
288: * @param fname The file that specifies an Image to load the texture with
289: * @param observer The associated image observer
290: *
291: * @exception ImageException if there is a problem reading the image
292: */
293: public TextureLoader(String fname, Component observer) {
294: this (fname, null, 0, observer);
295: }
296:
297: /**
298: * Contructs a TextureLoader object using the specified file,
299: * and format
300: * @param fname The file that specifies an Image to load the texture with
301: * @param format The format specifies which channels to use
302: * @param observer The associated image observer
303: *
304: * @exception ImageException if there is a problem reading the image
305: */
306: public TextureLoader(String fname, String format, Component observer) {
307: this (fname, format, 0, observer);
308: }
309:
310: /**
311: * Contructs a TextureLoader object using the specified file,
312: * option flags and default format RGBA
313: * @param fname The file that specifies an Image to load the texture with
314: * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
315: * @param observer The associated image observer
316: *
317: * @exception ImageException if there is a problem reading the image
318: */
319: public TextureLoader(String fname, int flags, Component observer) {
320: this (fname, null, flags, observer);
321: }
322:
323: /**
324: * Contructs a TextureLoader object using the specified file,
325: * format and option flags
326: * @param fname The file that specifies an Image to load the texture with
327: * @param format The format specifies which channels to use
328: * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
329: * @param observer The associated image observer
330: *
331: * @exception ImageException if there is a problem reading the image
332: */
333: public TextureLoader(final String fname, String format, int flags,
334: Component observer) {
335:
336: if (observer == null) {
337: observer = new java.awt.Container();
338: }
339:
340: bufferedImage = (BufferedImage) java.security.AccessController
341: .doPrivileged(new java.security.PrivilegedAction() {
342: public Object run() {
343: try {
344: return ImageIO.read(new File(fname));
345: } catch (IOException e) {
346: throw new ImageException(e);
347: }
348: }
349: });
350:
351: if (bufferedImage == null) {
352: throw new ImageException("Error loading image: " + fname);
353: }
354:
355: parseFormat(format);
356: this .flags = flags;
357:
358: if (format == null)
359: chooseFormat(bufferedImage);
360:
361: if ((flags & BY_REFERENCE) != 0) {
362: byRef = true;
363: }
364: if ((flags & Y_UP) != 0) {
365: yUp = true;
366: }
367: if ((flags & ALLOW_NON_POWER_OF_TWO) != 0) {
368: forcePowerOfTwo = false;
369: }
370: }
371:
372: /**
373: * Contructs a TextureLoader object using the specified URL
374: * and default format RGBA
375: * @param url The URL that specifies an Image to load the texture with
376: * @param observer The associated image observer
377: *
378: * @exception ImageException if there is a problem reading the image
379: */
380: public TextureLoader(URL url, Component observer) {
381: this (url, null, 0, observer);
382: }
383:
384: /**
385: * Contructs a TextureLoader object using the specified URL,
386: * and format
387: * @param url The URL that specifies an Image to load the texture with
388: * @param format The format specifies which channels to use
389: * @param observer The associated image observer
390: *
391: * @exception ImageException if there is a problem reading the image
392: */
393: public TextureLoader(URL url, String format, Component observer) {
394: this (url, format, 0, observer);
395: }
396:
397: /**
398: * Contructs a TextureLoader object using the specified URL,
399: * option flags and default format RGBA
400: * @param url The URL that specifies an Image to load the texture with
401: * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
402: * @param observer The associated image observer
403: *
404: * @exception ImageException if there is a problem reading the image
405: */
406: public TextureLoader(URL url, int flags, Component observer) {
407: this (url, null, flags, observer);
408: }
409:
410: /**
411: * Contructs a TextureLoader object using the specified URL,
412: * format and option flags
413: * @param url The url that specifies an Image to load the texture with
414: * @param format The format specifies which channels to use
415: * @param flags The flags specify what options to use in texture loading (generate mipmap etc)
416: * @param observer The associated image observer
417: *
418: * @exception ImageException if there is a problem reading the image
419: */
420: public TextureLoader(final URL url, String format, int flags,
421: Component observer) {
422:
423: if (observer == null) {
424: observer = new java.awt.Container();
425: }
426:
427: bufferedImage = (BufferedImage) java.security.AccessController
428: .doPrivileged(new java.security.PrivilegedAction() {
429: public Object run() {
430: try {
431: return ImageIO.read(url);
432: } catch (IOException e) {
433: throw new ImageException(e);
434: }
435: }
436: });
437:
438: if (bufferedImage == null) {
439: throw new ImageException("Error loading image: "
440: + url.toString());
441: }
442:
443: parseFormat(format);
444: this .flags = flags;
445:
446: if (format == null)
447: chooseFormat(bufferedImage);
448:
449: if ((flags & BY_REFERENCE) != 0) {
450: byRef = true;
451: }
452: if ((flags & Y_UP) != 0) {
453: yUp = true;
454: }
455: if ((flags & ALLOW_NON_POWER_OF_TWO) != 0) {
456: forcePowerOfTwo = false;
457: }
458: }
459:
460: /**
461: * Returns the associated ImageComponent2D object
462: *
463: * @return The associated ImageComponent2D object
464: */
465: public ImageComponent2D getImage() {
466: if (imageComponent == null)
467: imageComponent = new ImageComponent2D(imageComponentFormat,
468: bufferedImage, byRef, yUp);
469: return imageComponent;
470: }
471:
472: /**
473: * Returns the scaled ImageComponent2D object
474: *
475: * @param xScale The X scaling factor
476: * @param yScale The Y scaling factor
477: *
478: * @return The scaled ImageComponent2D object
479: */
480: public ImageComponent2D getScaledImage(float xScale, float yScale) {
481: if (xScale == 1.0f && yScale == 1.0f)
482: return getImage();
483: else
484: return (new ImageComponent2D(imageComponentFormat,
485: getScaledImage(bufferedImage, xScale, yScale),
486: byRef, yUp));
487: }
488:
489: /**
490: * Returns the scaled ImageComponent2D object
491: *
492: * @param width The desired width
493: * @param height The desired height
494: *
495: * @return The scaled ImageComponent2D object
496: */
497: public ImageComponent2D getScaledImage(int width, int height) {
498:
499: if (bufferedImage.getWidth() == width
500: && bufferedImage.getHeight() == height)
501: return getImage();
502: else
503: return (new ImageComponent2D(imageComponentFormat,
504: getScaledImage(bufferedImage, width, height),
505: byRef, yUp));
506: }
507:
508: /**
509: * Returns the associated Texture object.
510: *
511: * @return The associated Texture object
512: */
513: public Texture getTexture() {
514: ImageComponent2D[] scaledImageComponents = null;
515: BufferedImage[] scaledBufferedImages = null;
516: if (tex == null) {
517:
518: int width;
519: int height;
520:
521: if (forcePowerOfTwo) {
522: width = getClosestPowerOf2(bufferedImage.getWidth());
523: height = getClosestPowerOf2(bufferedImage.getHeight());
524: } else {
525: width = bufferedImage.getWidth();
526: height = bufferedImage.getHeight();
527: }
528:
529: if ((flags & GENERATE_MIPMAP) != 0) {
530:
531: BufferedImage origImage = bufferedImage;
532: int newW = width;
533: int newH = height;
534: int level = Math.max(computeLog(width),
535: computeLog(height)) + 1;
536: scaledImageComponents = new ImageComponent2D[level];
537: scaledBufferedImages = new BufferedImage[level];
538: tex = new Texture2D(tex.MULTI_LEVEL_MIPMAP,
539: textureFormat, width, height);
540:
541: for (int i = 0; i < level; i++) {
542: scaledBufferedImages[i] = getScaledImage(origImage,
543: newW, newH);
544: scaledImageComponents[i] = new ImageComponent2D(
545: imageComponentFormat,
546: scaledBufferedImages[i], byRef, yUp);
547:
548: tex.setImage(i, scaledImageComponents[i]);
549: if (forcePowerOfTwo) {
550: if (newW > 1)
551: newW >>= 1;
552: if (newH > 1)
553: newH >>= 1;
554: } else {
555: if (newW > 1) {
556: newW = (int) Math.floor(newW / 2.0);
557: }
558: if (newH > 1) {
559: newH = (int) Math.floor(newH / 2.0);
560: }
561: }
562: origImage = scaledBufferedImages[i];
563: }
564:
565: } else {
566: scaledImageComponents = new ImageComponent2D[1];
567: scaledBufferedImages = new BufferedImage[1];
568:
569: // Create texture from image
570: scaledBufferedImages[0] = getScaledImage(bufferedImage,
571: width, height);
572: scaledImageComponents[0] = new ImageComponent2D(
573: imageComponentFormat, scaledBufferedImages[0],
574: byRef, yUp);
575:
576: tex = new Texture2D(tex.BASE_LEVEL, textureFormat,
577: width, height);
578:
579: tex.setImage(0, scaledImageComponents[0]);
580: }
581: tex.setMinFilter(tex.BASE_LEVEL_LINEAR);
582: tex.setMagFilter(tex.BASE_LEVEL_LINEAR);
583: }
584:
585: return tex;
586: }
587:
588: // create a BufferedImage from an Image object
589: private BufferedImage createBufferedImage(Image image,
590: Component observer) {
591:
592: int status;
593:
594: observer.prepareImage(image, null);
595: while (true) {
596: status = observer.checkImage(image, null);
597: if ((status & ImageObserver.ERROR) != 0) {
598: return null;
599: } else if ((status & ImageObserver.ALLBITS) != 0) {
600: break;
601: }
602: try {
603: Thread.sleep(100);
604: } catch (InterruptedException e) {
605: }
606: }
607:
608: int width = image.getWidth(observer);
609: int height = image.getHeight(observer);
610:
611: WritableRaster wr = java.awt.image.Raster
612: .createInterleavedRaster(DataBuffer.TYPE_BYTE, width,
613: height, width * 4, 4, bandOffset, null);
614: BufferedImage bImage = new BufferedImage(colorModel, wr, false,
615: null);
616:
617: java.awt.Graphics g = bImage.getGraphics();
618: g.drawImage(image, 0, 0, observer);
619:
620: return bImage;
621: }
622:
623: /**
624: * Choose the correct ImageComponent and Texture format for the given
625: * image
626: */
627: private void chooseFormat(BufferedImage image) {
628: switch (image.getType()) {
629: case BufferedImage.TYPE_4BYTE_ABGR:
630: case BufferedImage.TYPE_INT_ARGB:
631: imageComponentFormat = ImageComponent.FORMAT_RGBA;
632: textureFormat = Texture.RGBA;
633: break;
634: case BufferedImage.TYPE_3BYTE_BGR:
635: case BufferedImage.TYPE_INT_BGR:
636: case BufferedImage.TYPE_INT_RGB:
637: imageComponentFormat = ImageComponent.FORMAT_RGB;
638: textureFormat = Texture.RGB;
639: break;
640: case BufferedImage.TYPE_CUSTOM:
641: if (is4ByteRGBAOr3ByteRGB(image)) {
642: SampleModel sm = image.getSampleModel();
643: if (sm.getNumBands() == 3) {
644: //System.out.println("ChooseFormat Custom:TYPE_4BYTE_ABGR");
645: imageComponentFormat = ImageComponent.FORMAT_RGB;
646: textureFormat = Texture.RGB;
647: } else {
648: imageComponentFormat = ImageComponent.FORMAT_RGBA;
649: //System.out.println("ChooseFormat Custom:TYPE_3BYTE_BGR");
650: textureFormat = Texture.RGBA;
651: }
652: }
653: break;
654: default:
655: // System.err.println("Unoptimized Image Type "+image.getType());
656: imageComponentFormat = ImageComponent.FORMAT_RGBA;
657: textureFormat = Texture.RGBA;
658: break;
659: }
660: }
661:
662: private boolean is4ByteRGBAOr3ByteRGB(RenderedImage ri) {
663: boolean value = false;
664: int i;
665: int biType = getImageType(ri);
666: if (biType != BufferedImage.TYPE_CUSTOM)
667: return false;
668: ColorModel cm = ri.getColorModel();
669: ColorSpace cs = cm.getColorSpace();
670: SampleModel sm = ri.getSampleModel();
671: boolean isAlphaPre = cm.isAlphaPremultiplied();
672: int csType = cs.getType();
673: if (csType == ColorSpace.TYPE_RGB) {
674: int numBands = sm.getNumBands();
675: if (sm.getDataType() == DataBuffer.TYPE_BYTE) {
676: if (cm instanceof ComponentColorModel
677: && sm instanceof PixelInterleavedSampleModel) {
678: PixelInterleavedSampleModel csm = (PixelInterleavedSampleModel) sm;
679: int[] offs = csm.getBandOffsets();
680: ComponentColorModel ccm = (ComponentColorModel) cm;
681: int[] nBits = ccm.getComponentSize();
682: boolean is8Bit = true;
683: for (i = 0; i < numBands; i++) {
684: if (nBits[i] != 8) {
685: is8Bit = false;
686: break;
687: }
688: }
689: if (is8Bit && offs[0] == 0 && offs[1] == 1
690: && offs[2] == 2) {
691: if (numBands == 3) {
692: value = true;
693: } else if (offs[3] == 3 && !isAlphaPre) {
694: value = true;
695: }
696: }
697: }
698: }
699: }
700: return value;
701: }
702:
703: private int getImageType(RenderedImage ri) {
704: int imageType = BufferedImage.TYPE_CUSTOM;
705: int i;
706:
707: if (ri instanceof BufferedImage) {
708: return ((BufferedImage) ri).getType();
709: }
710: ColorModel cm = ri.getColorModel();
711: ColorSpace cs = cm.getColorSpace();
712: SampleModel sm = ri.getSampleModel();
713: int csType = cs.getType();
714: boolean isAlphaPre = cm.isAlphaPremultiplied();
715: if (csType != ColorSpace.TYPE_RGB) {
716: if (csType == ColorSpace.TYPE_GRAY
717: && cm instanceof ComponentColorModel) {
718: if (sm.getDataType() == DataBuffer.TYPE_BYTE) {
719: imageType = BufferedImage.TYPE_BYTE_GRAY;
720: } else if (sm.getDataType() == DataBuffer.TYPE_USHORT) {
721: imageType = BufferedImage.TYPE_USHORT_GRAY;
722: }
723: }
724: }
725: // RGB , only interested in BYTE ABGR and BGR for now
726: // all others will be copied to a buffered image
727: else {
728: int numBands = sm.getNumBands();
729: if (sm.getDataType() == DataBuffer.TYPE_BYTE) {
730: if (cm instanceof ComponentColorModel
731: && sm instanceof PixelInterleavedSampleModel) {
732: PixelInterleavedSampleModel csm = (PixelInterleavedSampleModel) sm;
733: int[] offs = csm.getBandOffsets();
734: ComponentColorModel ccm = (ComponentColorModel) cm;
735: int[] nBits = ccm.getComponentSize();
736: boolean is8Bit = true;
737: for (i = 0; i < numBands; i++) {
738: if (nBits[i] != 8) {
739: is8Bit = false;
740: break;
741: }
742: }
743: if (is8Bit && offs[0] == numBands - 1
744: && offs[1] == numBands - 2
745: && offs[2] == numBands - 3) {
746: if (numBands == 3) {
747: imageType = BufferedImage.TYPE_3BYTE_BGR;
748: } else if (offs[3] == 0) {
749: imageType = (isAlphaPre ? BufferedImage.TYPE_4BYTE_ABGR_PRE
750: : BufferedImage.TYPE_4BYTE_ABGR);
751: }
752: }
753: }
754: }
755: }
756: return imageType;
757: }
758:
759: // initialize appropriate format for ImageComponent and Texture
760: private void parseFormat(String format) {
761: if (format == null)
762: return;
763:
764: if (format.equals("RGBA")) {
765: imageComponentFormat = ImageComponent.FORMAT_RGBA;
766: textureFormat = Texture.RGBA;
767:
768: } else if (format.equals("RGBA4")) {
769: imageComponentFormat = ImageComponent.FORMAT_RGBA4;
770: textureFormat = Texture.RGBA;
771:
772: } else if (format.equals("RGB5_A1")) {
773: imageComponentFormat = ImageComponent.FORMAT_RGB5_A1;
774: textureFormat = Texture.RGBA;
775:
776: } else if (format.equals("RGB")) {
777: imageComponentFormat = ImageComponent.FORMAT_RGB;
778: textureFormat = Texture.RGB;
779:
780: } else if (format.equals("RGB4")) {
781: imageComponentFormat = ImageComponent.FORMAT_RGB4;
782: textureFormat = Texture.RGB;
783:
784: } else if (format.equals("RGB5")) {
785: imageComponentFormat = ImageComponent.FORMAT_RGB5;
786: textureFormat = Texture.RGB;
787:
788: } else if (format.equals("R3_G3_B2")) {
789: imageComponentFormat = ImageComponent.FORMAT_R3_G3_B2;
790: textureFormat = Texture.RGB;
791:
792: } else if (format.equals("LUM8_ALPHA8")) {
793: imageComponentFormat = ImageComponent.FORMAT_LUM8_ALPHA8;
794: textureFormat = Texture.LUMINANCE_ALPHA;
795:
796: } else if (format.equals("LUM4_ALPHA4")) {
797: imageComponentFormat = ImageComponent.FORMAT_LUM4_ALPHA4;
798: textureFormat = Texture.LUMINANCE_ALPHA;
799:
800: } else if (format.equals("LUMINANCE")) {
801: imageComponentFormat = ImageComponent.FORMAT_CHANNEL8;
802: textureFormat = Texture.LUMINANCE;
803:
804: } else if (format.equals("ALPHA")) {
805: imageComponentFormat = ImageComponent.FORMAT_CHANNEL8;
806: textureFormat = Texture.ALPHA;
807: }
808: }
809:
810: // return a scaled image of given width and height
811: private BufferedImage getScaledImage(BufferedImage origImage,
812: int width, int height) {
813:
814: int origW = origImage.getWidth();
815: int origH = origImage.getHeight();
816: float xScale = (float) width / (float) origW;
817: float yScale = (float) height / (float) origH;
818:
819: return (getScaledImage(origImage, xScale, yScale));
820: }
821:
822: // return a scaled image of given x and y scale
823: private BufferedImage getScaledImage(BufferedImage origImage,
824: float xScale, float yScale) {
825:
826: // System.err.println("(1) origImage " + origImage);
827: // If the image is already the requested size, no need to scale
828: if (xScale == 1.0f && yScale == 1.0f)
829: return origImage;
830: else {
831: int scaleW = (int) (origImage.getWidth() * xScale + 0.5);
832: int scaleH = (int) (origImage.getHeight() * yScale + 0.5);
833:
834: int origImageType = origImage.getType();
835: BufferedImage scaledImage;
836: WritableRaster wr;
837:
838: if (origImageType != BufferedImage.TYPE_CUSTOM) {
839: WritableRaster origWr = origImage.getRaster();
840: wr = origWr.createCompatibleWritableRaster(0, 0,
841: scaleW, scaleH);
842: scaledImage = new BufferedImage(scaleW, scaleH,
843: origImageType);
844: } else {
845: int numComponents = origImage.getSampleModel()
846: .getNumBands();
847: int[] bandOffset = new int[numComponents];
848: int[] nBits = new int[numComponents];
849: for (int ii = 0; ii < numComponents; ii++) {
850: bandOffset[ii] = ii;
851: nBits[ii] = 8;
852: }
853:
854: wr = java.awt.image.Raster.createInterleavedRaster(
855: DataBuffer.TYPE_BYTE, scaleW, scaleH, scaleW
856: * numComponents, numComponents,
857: bandOffset, null);
858:
859: int imageType;
860:
861: switch (numComponents) {
862: case 1:
863: imageType = BufferedImage.TYPE_BYTE_GRAY;
864: break;
865: case 3:
866: imageType = BufferedImage.TYPE_3BYTE_BGR;
867: break;
868: case 4:
869: imageType = BufferedImage.TYPE_4BYTE_ABGR;
870: break;
871: default:
872: throw new ImageException(
873: "Illegal number of bands : "
874: + numComponents);
875:
876: }
877:
878: scaledImage = new BufferedImage(scaleW, scaleH,
879: imageType);
880: }
881:
882: scaledImage.setData(wr);
883: java.awt.Graphics2D g2 = scaledImage.createGraphics();
884: AffineTransform at = AffineTransform.getScaleInstance(
885: xScale, yScale);
886: g2.transform(at);
887: g2.drawImage(origImage, 0, 0, null);
888:
889: return scaledImage;
890: }
891: }
892:
893: private int computeLog(int value) {
894: int i = 0;
895:
896: if (value == 0)
897: return -1;
898: for (;;) {
899: if (value == 1)
900: return i;
901: value >>= 1;
902: i++;
903: }
904: }
905:
906: private int getClosestPowerOf2(int value) {
907:
908: if (value < 1)
909: return value;
910:
911: int powerValue = 1;
912: for (;;) {
913: powerValue *= 2;
914: if (value < powerValue) {
915: // Found max bound of power, determine which is closest
916: int minBound = powerValue / 2;
917: if ((powerValue - value) > (value - minBound))
918: return minBound;
919: else
920: return powerValue;
921: }
922: }
923: }
924: }
|