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