001/* 002 * $RCSfile: ImageUtil.java,v $ 003 * 004 * 005 * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without 008 * modification, are permitted provided that the following conditions 009 * are met: 010 * 011 * - Redistribution of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * 014 * - Redistribution in binary form must reproduce the above copyright 015 * notice, this list of conditions and the following disclaimer in 016 * the documentation and/or other materials provided with the 017 * distribution. 018 * 019 * Neither the name of Sun Microsystems, Inc. or the names of 020 * contributors may be used to endorse or promote products derived 021 * from this software without specific prior written permission. 022 * 023 * This software is provided "AS IS," without a warranty of any 024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS 030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, 032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR 034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 035 * POSSIBILITY OF SUCH DAMAGES. 036 * 037 * You acknowledge that this software is not designed or intended for 038 * use in the design, construction, operation or maintenance of any 039 * nuclear facility. 040 * 041 * $Revision: 1.7 $ 042 * $Date: 2007/08/28 18:45:06 $ 043 * $State: Exp $ 044 */ 045package com.github.jaiimageio.impl.common; 046 047import java.awt.Rectangle; 048import java.awt.Transparency; 049import java.awt.color.ColorSpace; 050import java.awt.color.ICC_ColorSpace; 051import java.awt.image.BufferedImage; 052import java.awt.image.ColorModel; 053import java.awt.image.ComponentColorModel; 054import java.awt.image.ComponentSampleModel; 055import java.awt.image.DataBuffer; 056import java.awt.image.DataBufferByte; 057import java.awt.image.DataBufferInt; 058import java.awt.image.DataBufferShort; 059import java.awt.image.DataBufferUShort; 060import java.awt.image.DirectColorModel; 061import java.awt.image.IndexColorModel; 062import java.awt.image.MultiPixelPackedSampleModel; 063import java.awt.image.Raster; 064import java.awt.image.RenderedImage; 065import java.awt.image.SampleModel; 066import java.awt.image.SinglePixelPackedSampleModel; 067import java.awt.image.WritableRaster; 068import java.io.IOException; 069import java.util.ArrayList; 070import java.util.Iterator; 071import java.util.List; 072import java.util.Locale; 073//import javax.imageio.ImageTypeSpecifier; 074import javax.imageio.stream.ImageInputStream; 075import javax.imageio.spi.ImageReaderWriterSpi; 076import javax.imageio.spi.ImageReaderSpi; 077import javax.imageio.spi.IIORegistry; 078import javax.imageio.spi.ServiceRegistry; 079import javax.imageio.ImageReadParam; 080import javax.imageio.spi.ImageWriterSpi; 081import javax.imageio.IIOException; 082import javax.imageio.ImageTypeSpecifier; 083import javax.imageio.ImageWriter; 084 085public class ImageUtil { 086 /* XXX testing only 087 public static void main(String[] args) { 088 ImageTypeSpecifier bilevel = 089 ImageTypeSpecifier.createIndexed(new byte[] {(byte)0, (byte)255}, 090 new byte[] {(byte)0, (byte)255}, 091 new byte[] {(byte)0, (byte)255}, 092 null, 1, 093 DataBuffer.TYPE_BYTE); 094 ImageTypeSpecifier gray = 095 ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false); 096 ImageTypeSpecifier grayAlpha = 097 ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false, 098 false); 099 ImageTypeSpecifier rgb = 100 ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), 101 new int[] {0, 1, 2}, 102 DataBuffer.TYPE_BYTE, 103 false, 104 false); 105 ImageTypeSpecifier rgba = 106 ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), 107 new int[] {0, 1, 2, 3}, 108 DataBuffer.TYPE_BYTE, 109 true, 110 false); 111 ImageTypeSpecifier packed = 112 ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB), 113 0xff000000, 114 0x00ff0000, 115 0x0000ff00, 116 0x000000ff, 117 DataBuffer.TYPE_BYTE, 118 false); 119 120 SampleModel bandedSM = 121 new java.awt.image.BandedSampleModel(DataBuffer.TYPE_BYTE, 122 1, 1, 15); 123 124 System.out.println(createColorModel(bilevel.getSampleModel())); 125 System.out.println(createColorModel(gray.getSampleModel())); 126 System.out.println(createColorModel(grayAlpha.getSampleModel())); 127 System.out.println(createColorModel(rgb.getSampleModel())); 128 System.out.println(createColorModel(rgba.getSampleModel())); 129 System.out.println(createColorModel(packed.getSampleModel())); 130 System.out.println(createColorModel(bandedSM)); 131 } 132 */ 133 134 /** 135 * Creates a <code>ColorModel</code> that may be used with the 136 * specified <code>SampleModel</code>. If a suitable 137 * <code>ColorModel</code> cannot be found, this method returns 138 * <code>null</code>. 139 * 140 * <p> Suitable <code>ColorModel</code>s are guaranteed to exist 141 * for all instances of <code>ComponentSampleModel</code>. 142 * For 1- and 3- banded <code>SampleModel</code>s, the returned 143 * <code>ColorModel</code> will be opaque. For 2- and 4-banded 144 * <code>SampleModel</code>s, the output will use alpha transparency 145 * which is not premultiplied. 1- and 2-banded data will use a 146 * grayscale <code>ColorSpace</code>, and 3- and 4-banded data a sRGB 147 * <code>ColorSpace</code>. Data with 5 or more bands will have a 148 * <code>BogusColorSpace</code>.</p> 149 * 150 * <p>An instance of <code>DirectColorModel</code> will be created for 151 * instances of <code>SinglePixelPackedSampleModel</code> with no more 152 * than 4 bands.</p> 153 * 154 * <p>An instance of <code>IndexColorModel</code> will be created for 155 * instances of <code>MultiPixelPackedSampleModel</code>. The colormap 156 * will be a grayscale ramp with <code>1 << numberOfBits</code> 157 * entries ranging from zero to at most 255.</p> 158 * 159 * @return An instance of <code>ColorModel</code> that is suitable for 160 * the supplied <code>SampleModel</code>, or <code>null</code>. 161 * 162 * @throws IllegalArgumentException If <code>sampleModel</code> is 163 * <code>null</code>. 164 */ 165 public static final ColorModel createColorModel(SampleModel sampleModel) { 166 // Check the parameter. 167 if(sampleModel == null) { 168 throw new IllegalArgumentException("sampleModel == null!"); 169 } 170 171 // Get the data type. 172 int dataType = sampleModel.getDataType(); 173 174 // Check the data type 175 switch(dataType) { 176 case DataBuffer.TYPE_BYTE: 177 case DataBuffer.TYPE_USHORT: 178 case DataBuffer.TYPE_SHORT: 179 case DataBuffer.TYPE_INT: 180 case DataBuffer.TYPE_FLOAT: 181 case DataBuffer.TYPE_DOUBLE: 182 break; 183 default: 184 // Return null for other types. 185 return null; 186 } 187 188 // The return variable. 189 ColorModel colorModel = null; 190 191 // Get the sample size. 192 int[] sampleSize = sampleModel.getSampleSize(); 193 194 // Create a Component ColorModel. 195 if(sampleModel instanceof ComponentSampleModel) { 196 // Get the number of bands. 197 int numBands = sampleModel.getNumBands(); 198 199 // Determine the color space. 200 ColorSpace colorSpace = null; 201 if(numBands <= 2) { 202 colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY); 203 } else if(numBands <= 4) { 204 colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); 205 } else { 206 colorSpace = new BogusColorSpace(numBands); 207 } 208 209 boolean hasAlpha = (numBands == 2) || (numBands == 4); 210 boolean isAlphaPremultiplied = false; 211 int transparency = hasAlpha ? 212 Transparency.TRANSLUCENT : Transparency.OPAQUE; 213 214 colorModel = new ComponentColorModel(colorSpace, 215 sampleSize, 216 hasAlpha, 217 isAlphaPremultiplied, 218 transparency, 219 dataType); 220 } else if (sampleModel.getNumBands() <= 4 && 221 sampleModel instanceof SinglePixelPackedSampleModel) { 222 SinglePixelPackedSampleModel sppsm = 223 (SinglePixelPackedSampleModel)sampleModel; 224 225 int[] bitMasks = sppsm.getBitMasks(); 226 int rmask = 0; 227 int gmask = 0; 228 int bmask = 0; 229 int amask = 0; 230 231 int numBands = bitMasks.length; 232 if (numBands <= 2) { 233 rmask = gmask = bmask = bitMasks[0]; 234 if (numBands == 2) { 235 amask = bitMasks[1]; 236 } 237 } else { 238 rmask = bitMasks[0]; 239 gmask = bitMasks[1]; 240 bmask = bitMasks[2]; 241 if (numBands == 4) { 242 amask = bitMasks[3]; 243 } 244 } 245 246 int bits = 0; 247 for (int i = 0; i < sampleSize.length; i++) { 248 bits += sampleSize[i]; 249 } 250 251 return new DirectColorModel(bits, rmask, gmask, bmask, amask); 252 253 } else if(sampleModel instanceof MultiPixelPackedSampleModel) { 254 // Load the colormap with a ramp. 255 int bitsPerSample = sampleSize[0]; 256 int numEntries = 1 << bitsPerSample; 257 byte[] map = new byte[numEntries]; 258 for (int i = 0; i < numEntries; i++) { 259 map[i] = (byte)(i*255/(numEntries - 1)); 260 } 261 262 colorModel = new IndexColorModel(bitsPerSample, numEntries, 263 map, map, map); 264 265 } 266 267 return colorModel; 268 } 269 270 /** 271 * For the case of binary data (<code>isBinary()</code> returns 272 * <code>true</code>), return the binary data as a packed byte array. 273 * The data will be packed as eight bits per byte with no bit offset, 274 * i.e., the first bit in each image line will be the left-most of the 275 * first byte of the line. The line stride in bytes will be 276 * <code>(int)((getWidth()+7)/8)</code>. The length of the returned 277 * array will be the line stride multiplied by <code>getHeight()</code> 278 * 279 * @return the binary data as a packed array of bytes with zero offset 280 * of <code>null</code> if the data are not binary. 281 * @throws IllegalArgumentException if <code>isBinary()</code> returns 282 * <code>false</code> with the <code>SampleModel</code> of the 283 * supplied <code>Raster</code> as argument. 284 */ 285 public static byte[] getPackedBinaryData(Raster raster, 286 Rectangle rect) { 287 SampleModel sm = raster.getSampleModel(); 288 if(!isBinary(sm)) { 289 throw new IllegalArgumentException(I18N.getString("ImageUtil0")); 290 } 291 292 int rectX = rect.x; 293 int rectY = rect.y; 294 int rectWidth = rect.width; 295 int rectHeight = rect.height; 296 297 DataBuffer dataBuffer = raster.getDataBuffer(); 298 299 int dx = rectX - raster.getSampleModelTranslateX(); 300 int dy = rectY - raster.getSampleModelTranslateY(); 301 302 MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; 303 int lineStride = mpp.getScanlineStride(); 304 int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); 305 int bitOffset = mpp.getBitOffset(dx); 306 307 int numBytesPerRow = (rectWidth + 7)/8; 308 if(dataBuffer instanceof DataBufferByte && 309 eltOffset == 0 && bitOffset == 0 && 310 numBytesPerRow == lineStride && 311 ((DataBufferByte)dataBuffer).getData().length == 312 numBytesPerRow*rectHeight) { 313 return ((DataBufferByte)dataBuffer).getData(); 314 } 315 316 byte[] binaryDataArray = new byte[numBytesPerRow*rectHeight]; 317 318 int b = 0; 319 320 if(bitOffset == 0) { 321 if(dataBuffer instanceof DataBufferByte) { 322 byte[] data = ((DataBufferByte)dataBuffer).getData(); 323 int stride = numBytesPerRow; 324 int offset = 0; 325 for(int y = 0; y < rectHeight; y++) { 326 System.arraycopy(data, eltOffset, 327 binaryDataArray, offset, 328 stride); 329 offset += stride; 330 eltOffset += lineStride; 331 } 332 } else if(dataBuffer instanceof DataBufferShort || 333 dataBuffer instanceof DataBufferUShort) { 334 short[] data = dataBuffer instanceof DataBufferShort ? 335 ((DataBufferShort)dataBuffer).getData() : 336 ((DataBufferUShort)dataBuffer).getData(); 337 338 for(int y = 0; y < rectHeight; y++) { 339 int xRemaining = rectWidth; 340 int i = eltOffset; 341 while(xRemaining > 8) { 342 short datum = data[i++]; 343 binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF); 344 binaryDataArray[b++] = (byte)(datum & 0xFF); 345 xRemaining -= 16; 346 } 347 if(xRemaining > 0) { 348 binaryDataArray[b++] = (byte)((data[i] >>> 8) & 0XFF); 349 } 350 eltOffset += lineStride; 351 } 352 } else if(dataBuffer instanceof DataBufferInt) { 353 int[] data = ((DataBufferInt)dataBuffer).getData(); 354 355 for(int y = 0; y < rectHeight; y++) { 356 int xRemaining = rectWidth; 357 int i = eltOffset; 358 while(xRemaining > 24) { 359 int datum = data[i++]; 360 binaryDataArray[b++] = (byte)((datum >>> 24) & 0xFF); 361 binaryDataArray[b++] = (byte)((datum >>> 16) & 0xFF); 362 binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF); 363 binaryDataArray[b++] = (byte)(datum & 0xFF); 364 xRemaining -= 32; 365 } 366 int shift = 24; 367 while(xRemaining > 0) { 368 binaryDataArray[b++] = 369 (byte)((data[i] >>> shift) & 0xFF); 370 shift -= 8; 371 xRemaining -= 8; 372 } 373 eltOffset += lineStride; 374 } 375 } 376 } else { // bitOffset != 0 377 if(dataBuffer instanceof DataBufferByte) { 378 byte[] data = ((DataBufferByte)dataBuffer).getData(); 379 380 if((bitOffset & 7) == 0) { 381 int stride = numBytesPerRow; 382 int offset = 0; 383 for(int y = 0; y < rectHeight; y++) { 384 System.arraycopy(data, eltOffset, 385 binaryDataArray, offset, 386 stride); 387 offset += stride; 388 eltOffset += lineStride; 389 } 390 } else { // bitOffset % 8 != 0 391 int leftShift = bitOffset & 7; 392 int rightShift = 8 - leftShift; 393 for(int y = 0; y < rectHeight; y++) { 394 int i = eltOffset; 395 int xRemaining = rectWidth; 396 while(xRemaining > 0) { 397 if(xRemaining > rightShift) { 398 binaryDataArray[b++] = 399 (byte)(((data[i++]&0xFF) << leftShift) | 400 ((data[i]&0xFF) >>> rightShift)); 401 } else { 402 binaryDataArray[b++] = 403 (byte)((data[i]&0xFF) << leftShift); 404 } 405 xRemaining -= 8; 406 } 407 eltOffset += lineStride; 408 } 409 } 410 } else if(dataBuffer instanceof DataBufferShort || 411 dataBuffer instanceof DataBufferUShort) { 412 short[] data = dataBuffer instanceof DataBufferShort ? 413 ((DataBufferShort)dataBuffer).getData() : 414 ((DataBufferUShort)dataBuffer).getData(); 415 416 for(int y = 0; y < rectHeight; y++) { 417 int bOffset = bitOffset; 418 for(int x = 0; x < rectWidth; x += 8, bOffset += 8) { 419 int i = eltOffset + bOffset/16; 420 int mod = bOffset % 16; 421 int left = data[i] & 0xFFFF; 422 if(mod <= 8) { 423 binaryDataArray[b++] = (byte)(left >>> (8 - mod)); 424 } else { 425 int delta = mod - 8; 426 int right = data[i+1] & 0xFFFF; 427 binaryDataArray[b++] = 428 (byte)((left << delta) | 429 (right >>> (16 - delta))); 430 } 431 } 432 eltOffset += lineStride; 433 } 434 } else if(dataBuffer instanceof DataBufferInt) { 435 int[] data = ((DataBufferInt)dataBuffer).getData(); 436 437 for(int y = 0; y < rectHeight; y++) { 438 int bOffset = bitOffset; 439 for(int x = 0; x < rectWidth; x += 8, bOffset += 8) { 440 int i = eltOffset + bOffset/32; 441 int mod = bOffset % 32; 442 int left = data[i]; 443 if(mod <= 24) { 444 binaryDataArray[b++] = 445 (byte)(left >>> (24 - mod)); 446 } else { 447 int delta = mod - 24; 448 int right = data[i+1]; 449 binaryDataArray[b++] = 450 (byte)((left << delta) | 451 (right >>> (32 - delta))); 452 } 453 } 454 eltOffset += lineStride; 455 } 456 } 457 } 458 459 return binaryDataArray; 460 } 461 462 /** 463 * Returns the binary data unpacked into an array of bytes. 464 * The line stride will be the width of the <code>Raster</code>. 465 * 466 * @throws IllegalArgumentException if <code>isBinary()</code> returns 467 * <code>false</code> with the <code>SampleModel</code> of the 468 * supplied <code>Raster</code> as argument. 469 */ 470 public static byte[] getUnpackedBinaryData(Raster raster, 471 Rectangle rect) { 472 SampleModel sm = raster.getSampleModel(); 473 if(!isBinary(sm)) { 474 throw new IllegalArgumentException(I18N.getString("ImageUtil0")); 475 } 476 477 int rectX = rect.x; 478 int rectY = rect.y; 479 int rectWidth = rect.width; 480 int rectHeight = rect.height; 481 482 DataBuffer dataBuffer = raster.getDataBuffer(); 483 484 int dx = rectX - raster.getSampleModelTranslateX(); 485 int dy = rectY - raster.getSampleModelTranslateY(); 486 487 MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; 488 int lineStride = mpp.getScanlineStride(); 489 int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); 490 int bitOffset = mpp.getBitOffset(dx); 491 492 byte[] bdata = new byte[rectWidth*rectHeight]; 493 int maxY = rectY + rectHeight; 494 int maxX = rectX + rectWidth; 495 int k = 0; 496 497 if(dataBuffer instanceof DataBufferByte) { 498 byte[] data = ((DataBufferByte)dataBuffer).getData(); 499 for(int y = rectY; y < maxY; y++) { 500 int bOffset = eltOffset*8 + bitOffset; 501 for(int x = rectX; x < maxX; x++) { 502 byte b = data[bOffset/8]; 503 bdata[k++] = 504 (byte)((b >>> (7 - bOffset & 7)) & 0x0000001); 505 bOffset++; 506 } 507 eltOffset += lineStride; 508 } 509 } else if(dataBuffer instanceof DataBufferShort || 510 dataBuffer instanceof DataBufferUShort) { 511 short[] data = dataBuffer instanceof DataBufferShort ? 512 ((DataBufferShort)dataBuffer).getData() : 513 ((DataBufferUShort)dataBuffer).getData(); 514 for(int y = rectY; y < maxY; y++) { 515 int bOffset = eltOffset*16 + bitOffset; 516 for(int x = rectX; x < maxX; x++) { 517 short s = data[bOffset/16]; 518 bdata[k++] = 519 (byte)((s >>> (15 - bOffset % 16)) & 520 0x0000001); 521 bOffset++; 522 } 523 eltOffset += lineStride; 524 } 525 } else if(dataBuffer instanceof DataBufferInt) { 526 int[] data = ((DataBufferInt)dataBuffer).getData(); 527 for(int y = rectY; y < maxY; y++) { 528 int bOffset = eltOffset*32 + bitOffset; 529 for(int x = rectX; x < maxX; x++) { 530 int i = data[bOffset/32]; 531 bdata[k++] = 532 (byte)((i >>> (31 - bOffset % 32)) & 533 0x0000001); 534 bOffset++; 535 } 536 eltOffset += lineStride; 537 } 538 } 539 540 return bdata; 541 } 542 543 /** 544 * Sets the supplied <code>Raster</code>'s data from an array 545 * of packed binary data of the form returned by 546 * <code>getPackedBinaryData()</code>. 547 * 548 * @throws IllegalArgumentException if <code>isBinary()</code> returns 549 * <code>false</code> with the <code>SampleModel</code> of the 550 * supplied <code>Raster</code> as argument. 551 */ 552 public static void setPackedBinaryData(byte[] binaryDataArray, 553 WritableRaster raster, 554 Rectangle rect) { 555 SampleModel sm = raster.getSampleModel(); 556 if(!isBinary(sm)) { 557 throw new IllegalArgumentException(I18N.getString("ImageUtil0")); 558 } 559 560 int rectX = rect.x; 561 int rectY = rect.y; 562 int rectWidth = rect.width; 563 int rectHeight = rect.height; 564 565 DataBuffer dataBuffer = raster.getDataBuffer(); 566 567 int dx = rectX - raster.getSampleModelTranslateX(); 568 int dy = rectY - raster.getSampleModelTranslateY(); 569 570 MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; 571 int lineStride = mpp.getScanlineStride(); 572 int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); 573 int bitOffset = mpp.getBitOffset(dx); 574 575 int b = 0; 576 577 if(bitOffset == 0) { 578 if(dataBuffer instanceof DataBufferByte) { 579 byte[] data = ((DataBufferByte)dataBuffer).getData(); 580 if(data == binaryDataArray) { 581 // Optimal case: simply return. 582 return; 583 } 584 int stride = (rectWidth + 7)/8; 585 int offset = 0; 586 for(int y = 0; y < rectHeight; y++) { 587 System.arraycopy(binaryDataArray, offset, 588 data, eltOffset, 589 stride); 590 offset += stride; 591 eltOffset += lineStride; 592 } 593 } else if(dataBuffer instanceof DataBufferShort || 594 dataBuffer instanceof DataBufferUShort) { 595 short[] data = dataBuffer instanceof DataBufferShort ? 596 ((DataBufferShort)dataBuffer).getData() : 597 ((DataBufferUShort)dataBuffer).getData(); 598 599 for(int y = 0; y < rectHeight; y++) { 600 int xRemaining = rectWidth; 601 int i = eltOffset; 602 while(xRemaining > 8) { 603 data[i++] = 604 (short)(((binaryDataArray[b++] & 0xFF) << 8) | 605 (binaryDataArray[b++] & 0xFF)); 606 xRemaining -= 16; 607 } 608 if(xRemaining > 0) { 609 data[i++] = 610 (short)((binaryDataArray[b++] & 0xFF) << 8); 611 } 612 eltOffset += lineStride; 613 } 614 } else if(dataBuffer instanceof DataBufferInt) { 615 int[] data = ((DataBufferInt)dataBuffer).getData(); 616 617 for(int y = 0; y < rectHeight; y++) { 618 int xRemaining = rectWidth; 619 int i = eltOffset; 620 while(xRemaining > 24) { 621 data[i++] = 622 (int)(((binaryDataArray[b++] & 0xFF) << 24) | 623 ((binaryDataArray[b++] & 0xFF) << 16) | 624 ((binaryDataArray[b++] & 0xFF) << 8) | 625 (binaryDataArray[b++] & 0xFF)); 626 xRemaining -= 32; 627 } 628 int shift = 24; 629 while(xRemaining > 0) { 630 data[i] |= 631 (int)((binaryDataArray[b++] & 0xFF) << shift); 632 shift -= 8; 633 xRemaining -= 8; 634 } 635 eltOffset += lineStride; 636 } 637 } 638 } else { // bitOffset != 0 639 int stride = (rectWidth + 7)/8; 640 int offset = 0; 641 if(dataBuffer instanceof DataBufferByte) { 642 byte[] data = ((DataBufferByte)dataBuffer).getData(); 643 644 if((bitOffset & 7) == 0) { 645 for(int y = 0; y < rectHeight; y++) { 646 System.arraycopy(binaryDataArray, offset, 647 data, eltOffset, 648 stride); 649 offset += stride; 650 eltOffset += lineStride; 651 } 652 } else { // bitOffset % 8 != 0 653 int rightShift = bitOffset & 7; 654 int leftShift = 8 - rightShift; 655 int leftShift8 = 8 + leftShift; 656 int mask = (byte)(255<<leftShift); 657 int mask1 = (byte)~mask; 658 659 for(int y = 0; y < rectHeight; y++) { 660 int i = eltOffset; 661 int xRemaining = rectWidth; 662 while(xRemaining > 0) { 663 byte datum = binaryDataArray[b++]; 664 665 if (xRemaining > leftShift8) { 666 // when all the bits in this BYTE will be set 667 // into the data buffer. 668 data[i] = (byte)((data[i] & mask ) | 669 ((datum&0xFF) >>> rightShift)); 670 data[++i] = (byte)((datum & 0xFF) << leftShift); 671 } else if (xRemaining > leftShift) { 672 // All the "leftShift" high bits will be set 673 // into the data buffer. But not all the 674 // "rightShift" low bits will be set. 675 data[i] = (byte)((data[i] & mask ) | 676 ((datum&0xFF) >>> rightShift)); 677 i++; 678 data[i] = 679 (byte)((data[i] & mask1) | ((datum & 0xFF) << leftShift)); 680 } 681 else { 682 // Less than "leftShift" high bits will be set. 683 int remainMask = (1 << leftShift - xRemaining) - 1; 684 data[i] = 685 (byte)((data[i] & (mask | remainMask)) | 686 (datum&0xFF) >>> rightShift & ~remainMask); 687 } 688 xRemaining -= 8; 689 } 690 eltOffset += lineStride; 691 } 692 } 693 } else if(dataBuffer instanceof DataBufferShort || 694 dataBuffer instanceof DataBufferUShort) { 695 short[] data = dataBuffer instanceof DataBufferShort ? 696 ((DataBufferShort)dataBuffer).getData() : 697 ((DataBufferUShort)dataBuffer).getData(); 698 699 int rightShift = bitOffset & 7; 700 int leftShift = 8 - rightShift; 701 int leftShift16 = 16 + leftShift; 702 int mask = (short)(~(255 << leftShift)); 703 int mask1 = (short)(65535 << leftShift); 704 int mask2 = (short)~mask1; 705 706 for(int y = 0; y < rectHeight; y++) { 707 int bOffset = bitOffset; 708 int xRemaining = rectWidth; 709 for(int x = 0; x < rectWidth; 710 x += 8, bOffset += 8, xRemaining -= 8) { 711 int i = eltOffset + (bOffset >> 4); 712 int mod = bOffset & 15; 713 int datum = binaryDataArray[b++] & 0xFF; 714 if(mod <= 8) { 715 // This BYTE is set into one SHORT 716 if (xRemaining < 8) { 717 // Mask the bits to be set. 718 datum &= 255 << 8 - xRemaining; 719 } 720 data[i] = (short)((data[i] & mask) | (datum << leftShift)); 721 } else if (xRemaining > leftShift16) { 722 // This BYTE will be set into two SHORTs 723 data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF)); 724 data[++i] = 725 (short)((datum << leftShift)&0xFFFF); 726 } else if (xRemaining > leftShift) { 727 // This BYTE will be set into two SHORTs; 728 // But not all the low bits will be set into SHORT 729 data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF)); 730 i++; 731 data[i] = 732 (short)((data[i] & mask2) | ((datum << leftShift)&0xFFFF)); 733 } else { 734 // Only some of the high bits will be set into 735 // SHORTs 736 int remainMask = (1 << leftShift - xRemaining) - 1; 737 data[i] = (short)((data[i] & (mask1 | remainMask)) | 738 ((datum >>> rightShift)&0xFFFF & ~remainMask)); 739 } 740 } 741 eltOffset += lineStride; 742 } 743 } else if(dataBuffer instanceof DataBufferInt) { 744 int[] data = ((DataBufferInt)dataBuffer).getData(); 745 int rightShift = bitOffset & 7; 746 int leftShift = 8 - rightShift; 747 int leftShift32 = 32 + leftShift; 748 int mask = 0xFFFFFFFF << leftShift; 749 int mask1 = ~mask; 750 751 for(int y = 0; y < rectHeight; y++) { 752 int bOffset = bitOffset; 753 int xRemaining = rectWidth; 754 for(int x = 0; x < rectWidth; 755 x += 8, bOffset += 8, xRemaining -= 8) { 756 int i = eltOffset + (bOffset >> 5); 757 int mod = bOffset & 31; 758 int datum = binaryDataArray[b++] & 0xFF; 759 if(mod <= 24) { 760 // This BYTE is set into one INT 761 int shift = 24 - mod; 762 if (xRemaining < 8) { 763 // Mask the bits to be set. 764 datum &= 255 << 8 - xRemaining; 765 } 766 data[i] = (data[i] & (~(255 << shift))) | (datum << shift); 767 } else if (xRemaining > leftShift32) { 768 // All the bits of this BYTE will be set into two INTs 769 data[i] = (data[i] & mask) | (datum >>> rightShift); 770 data[++i] = datum << leftShift; 771 } else if (xRemaining > leftShift) { 772 // This BYTE will be set into two INTs; 773 // But not all the low bits will be set into INT 774 data[i] = (data[i] & mask) | (datum >>> rightShift); 775 i++; 776 data[i] = (data[i] & mask1) | (datum << leftShift); 777 } else { 778 // Only some of the high bits will be set into INT 779 int remainMask = (1 << leftShift - xRemaining) - 1; 780 data[i] = (data[i] & (mask | remainMask)) | 781 (datum >>> rightShift & ~remainMask); 782 } 783 } 784 eltOffset += lineStride; 785 } 786 } 787 } 788 } 789 790 /** 791 * Copies data into the packed array of the <code>Raster</code> 792 * from an array of unpacked data of the form returned by 793 * <code>getUnpackedBinaryData()</code>. 794 * 795 * <p> If the data are binary, then the target bit will be set if 796 * and only if the corresponding byte is non-zero. 797 * 798 * @throws IllegalArgumentException if <code>isBinary()</code> returns 799 * <code>false</code> with the <code>SampleModel</code> of the 800 * supplied <code>Raster</code> as argument. 801 */ 802 public static void setUnpackedBinaryData(byte[] bdata, 803 WritableRaster raster, 804 Rectangle rect) { 805 SampleModel sm = raster.getSampleModel(); 806 if(!isBinary(sm)) { 807 throw new IllegalArgumentException(I18N.getString("ImageUtil0")); 808 } 809 810 int rectX = rect.x; 811 int rectY = rect.y; 812 int rectWidth = rect.width; 813 int rectHeight = rect.height; 814 815 DataBuffer dataBuffer = raster.getDataBuffer(); 816 817 int dx = rectX - raster.getSampleModelTranslateX(); 818 int dy = rectY - raster.getSampleModelTranslateY(); 819 820 MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; 821 int lineStride = mpp.getScanlineStride(); 822 int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); 823 int bitOffset = mpp.getBitOffset(dx); 824 825 int k = 0; 826 827 if(dataBuffer instanceof DataBufferByte) { 828 byte[] data = ((DataBufferByte)dataBuffer).getData(); 829 for(int y = 0; y < rectHeight; y++) { 830 int bOffset = eltOffset*8 + bitOffset; 831 for(int x = 0; x < rectWidth; x++) { 832 if(bdata[k++] != (byte)0) { 833 data[bOffset/8] |= 834 (byte)(0x00000001 << (7 - bOffset & 7)); 835 } 836 bOffset++; 837 } 838 eltOffset += lineStride; 839 } 840 } else if(dataBuffer instanceof DataBufferShort || 841 dataBuffer instanceof DataBufferUShort) { 842 short[] data = dataBuffer instanceof DataBufferShort ? 843 ((DataBufferShort)dataBuffer).getData() : 844 ((DataBufferUShort)dataBuffer).getData(); 845 for(int y = 0; y < rectHeight; y++) { 846 int bOffset = eltOffset*16 + bitOffset; 847 for(int x = 0; x < rectWidth; x++) { 848 if(bdata[k++] != (byte)0) { 849 data[bOffset/16] |= 850 (short)(0x00000001 << 851 (15 - bOffset % 16)); 852 } 853 bOffset++; 854 } 855 eltOffset += lineStride; 856 } 857 } else if(dataBuffer instanceof DataBufferInt) { 858 int[] data = ((DataBufferInt)dataBuffer).getData(); 859 for(int y = 0; y < rectHeight; y++) { 860 int bOffset = eltOffset*32 + bitOffset; 861 for(int x = 0; x < rectWidth; x++) { 862 if(bdata[k++] != (byte)0) { 863 data[bOffset/32] |= 864 (int)(0x00000001 << 865 (31 - bOffset % 32)); 866 } 867 bOffset++; 868 } 869 eltOffset += lineStride; 870 } 871 } 872 } 873 874 public static boolean isBinary(SampleModel sm) { 875 return sm instanceof MultiPixelPackedSampleModel && 876 ((MultiPixelPackedSampleModel)sm).getPixelBitStride() == 1 && 877 sm.getNumBands() == 1; 878 } 879 880 public static ColorModel createColorModel(ColorSpace colorSpace, 881 SampleModel sampleModel) { 882 ColorModel colorModel = null; 883 884 if(sampleModel == null) { 885 throw new IllegalArgumentException(I18N.getString("ImageUtil1")); 886 } 887 888 int numBands = sampleModel.getNumBands(); 889 if (numBands < 1 || numBands > 4) { 890 return null; 891 } 892 893 int dataType = sampleModel.getDataType(); 894 if (sampleModel instanceof ComponentSampleModel) { 895 if (dataType < DataBuffer.TYPE_BYTE || 896 //dataType == DataBuffer.TYPE_SHORT || 897 dataType > DataBuffer.TYPE_DOUBLE) { 898 return null; 899 } 900 901 if (colorSpace == null) 902 colorSpace = 903 numBands <= 2 ? 904 ColorSpace.getInstance(ColorSpace.CS_GRAY) : 905 ColorSpace.getInstance(ColorSpace.CS_sRGB); 906 907 boolean useAlpha = (numBands == 2) || (numBands == 4); 908 int transparency = useAlpha ? 909 Transparency.TRANSLUCENT : Transparency.OPAQUE; 910 911 boolean premultiplied = false; 912 913 int dataTypeSize = DataBuffer.getDataTypeSize(dataType); 914 int[] bits = new int[numBands]; 915 for (int i = 0; i < numBands; i++) { 916 bits[i] = dataTypeSize; 917 } 918 919 colorModel = new ComponentColorModel(colorSpace, 920 bits, 921 useAlpha, 922 premultiplied, 923 transparency, 924 dataType); 925 } else if (sampleModel instanceof SinglePixelPackedSampleModel) { 926 SinglePixelPackedSampleModel sppsm = 927 (SinglePixelPackedSampleModel)sampleModel; 928 929 int[] bitMasks = sppsm.getBitMasks(); 930 int rmask = 0; 931 int gmask = 0; 932 int bmask = 0; 933 int amask = 0; 934 935 numBands = bitMasks.length; 936 if (numBands <= 2) { 937 rmask = gmask = bmask = bitMasks[0]; 938 if (numBands == 2) { 939 amask = bitMasks[1]; 940 } 941 } else { 942 rmask = bitMasks[0]; 943 gmask = bitMasks[1]; 944 bmask = bitMasks[2]; 945 if (numBands == 4) { 946 amask = bitMasks[3]; 947 } 948 } 949 950 int[] sampleSize = sppsm.getSampleSize(); 951 int bits = 0; 952 for (int i = 0; i < sampleSize.length; i++) { 953 bits += sampleSize[i]; 954 } 955 956 if (colorSpace == null) 957 colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); 958 959 colorModel = 960 new DirectColorModel(colorSpace, 961 bits, rmask, gmask, bmask, amask, 962 false, 963 sampleModel.getDataType()); 964 } else if (sampleModel instanceof MultiPixelPackedSampleModel) { 965 int bits = 966 ((MultiPixelPackedSampleModel)sampleModel).getPixelBitStride(); 967 int size = 1 << bits; 968 byte[] comp = new byte[size]; 969 970 for (int i = 0; i < size; i++) 971 comp[i] = (byte)(255 * i / (size - 1)); 972 973 colorModel = new IndexColorModel(bits, size, comp, comp, comp); 974 } 975 976 return colorModel; 977 } 978 979 public static int getElementSize(SampleModel sm) { 980 int elementSize = DataBuffer.getDataTypeSize(sm.getDataType()); 981 982 if (sm instanceof MultiPixelPackedSampleModel) { 983 MultiPixelPackedSampleModel mppsm = 984 (MultiPixelPackedSampleModel)sm; 985 return mppsm.getSampleSize(0) * mppsm.getNumBands(); 986 } else if (sm instanceof ComponentSampleModel) { 987 return sm.getNumBands() * elementSize; 988 } else if (sm instanceof SinglePixelPackedSampleModel) { 989 return elementSize; 990 } 991 992 return elementSize * sm.getNumBands(); 993 994 } 995 996 public static long getTileSize(SampleModel sm) { 997 int elementSize = DataBuffer.getDataTypeSize(sm.getDataType()); 998 999 if (sm instanceof MultiPixelPackedSampleModel) { 1000 MultiPixelPackedSampleModel mppsm = 1001 (MultiPixelPackedSampleModel)sm; 1002 return (mppsm.getScanlineStride() * mppsm.getHeight() + 1003 (mppsm.getDataBitOffset() + elementSize -1) / elementSize) * 1004 ((elementSize + 7) / 8); 1005 } else if (sm instanceof ComponentSampleModel) { 1006 ComponentSampleModel csm = (ComponentSampleModel)sm; 1007 int[] bandOffsets = csm.getBandOffsets(); 1008 int maxBandOff = bandOffsets[0]; 1009 for (int i=1; i<bandOffsets.length; i++) 1010 maxBandOff = Math.max(maxBandOff, bandOffsets[i]); 1011 1012 long size = 0; 1013 int pixelStride = csm.getPixelStride(); 1014 int scanlineStride = csm.getScanlineStride(); 1015 if (maxBandOff >= 0) 1016 size += maxBandOff + 1; 1017 if (pixelStride > 0) 1018 size += pixelStride * (sm.getWidth() - 1); 1019 if (scanlineStride > 0) 1020 size += scanlineStride * (sm.getHeight() - 1); 1021 1022 int[] bankIndices = csm.getBankIndices(); 1023 maxBandOff = bankIndices[0]; 1024 for (int i=1; i<bankIndices.length; i++) 1025 maxBandOff = Math.max(maxBandOff, bankIndices[i]); 1026 return size * (maxBandOff + 1) * ((elementSize + 7) / 8); 1027 } else if (sm instanceof SinglePixelPackedSampleModel) { 1028 SinglePixelPackedSampleModel sppsm = 1029 (SinglePixelPackedSampleModel)sm; 1030 long size = sppsm.getScanlineStride() * (sppsm.getHeight() - 1) + 1031 sppsm.getWidth(); 1032 return size * ((elementSize + 7) / 8); 1033 } 1034 1035 return 0; 1036 } 1037 1038 public static long getBandSize(SampleModel sm) { 1039 int elementSize = DataBuffer.getDataTypeSize(sm.getDataType()); 1040 1041 if (sm instanceof ComponentSampleModel) { 1042 ComponentSampleModel csm = (ComponentSampleModel)sm; 1043 int pixelStride = csm.getPixelStride(); 1044 int scanlineStride = csm.getScanlineStride(); 1045 long size = Math.min(pixelStride, scanlineStride); 1046 1047 if (pixelStride > 0) 1048 size += pixelStride * (sm.getWidth() - 1); 1049 if (scanlineStride > 0) 1050 size += scanlineStride * (sm.getHeight() - 1); 1051 return size * ((elementSize + 7) / 8); 1052 } else 1053 return getTileSize(sm); 1054 } 1055 1056 /** 1057 * Tests whether the color indices represent a gray-scale image with 1058 * the indicated number of bits over the color component range [0,255]. 1059 * The grayscale mapping may be inverted, i.e., 0 -> 255 and 1060 * mapSize -> 0. 1061 * 1062 * @param icm The gray-to-color mapping. 1063 * @return Whether the <code>IndexColorModel</code> maps index 1064 * <code>i</code> to <code>((255*i)/icm.getMapSize()-1)</code>. 1065 * @throws IllegalArgumentException if <code>icm</code> is 1066 * <code>null</code>. 1067 */ 1068 public static boolean isGrayscaleMapping(IndexColorModel icm) { 1069 if(icm == null) { 1070 throw new IllegalArgumentException("icm == null!"); 1071 } 1072 1073 // Get colormap size and contents. 1074 int mapSize = icm.getMapSize(); 1075 1076 byte[] r = new byte[mapSize]; 1077 byte[] g = new byte[mapSize]; 1078 byte[] b = new byte[mapSize]; 1079 1080 icm.getReds(r); 1081 icm.getGreens(g); 1082 icm.getBlues(b); 1083 1084 boolean isGrayToColor = true; 1085 1086 // Check ascending ramp. 1087 for (int i = 0; i < mapSize; i++) { 1088 byte temp = (byte)(i*255/(mapSize - 1)); 1089 1090 if (r[i] != temp || g[i] != temp || b[i] != temp) { 1091 isGrayToColor = false; 1092 break; 1093 } 1094 } 1095 1096 if(!isGrayToColor) { 1097 isGrayToColor = true; 1098 1099 // Check descending ramp. 1100 for (int i = 0, j = mapSize - 1; i < mapSize; i++, j--) { 1101 byte temp = (byte)(j*255/(mapSize - 1)); 1102 1103 if (r[i] != temp || g[i] != temp || b[i] != temp) { 1104 isGrayToColor = false; 1105 break; 1106 } 1107 } 1108 } 1109 1110 return isGrayToColor; 1111 } 1112 1113 /** 1114 * Tests whether the color indices represent a gray-scale image. 1115 * 1116 * @param r The red channel color indices. 1117 * @param g The green channel color indices. 1118 * @param b The blue channel color indices. 1119 * @return If all the indices have 256 entries, and are identical mappings, 1120 * return <code>true</code>; otherwise, return <code>false</code>. 1121 */ 1122 public static boolean isIndicesForGrayscale(byte[] r, byte[] g, byte[] b) { 1123 if (r.length != g.length || r.length != b.length) 1124 return false; 1125 1126 int size = r.length; 1127 1128 if (size != 256) 1129 return false; 1130 1131 for (int i = 0; i < size; i++) { 1132 byte temp = (byte) i; 1133 1134 if (r[i] != temp || g[i] != temp || b[i] != temp) 1135 return false; 1136 } 1137 1138 return true; 1139 } 1140 1141 /** Converts the provided object to <code>String</code> */ 1142 public static String convertObjectToString(Object obj) { 1143 if (obj == null) 1144 return ""; 1145 1146 String s = ""; 1147 if (obj instanceof byte[]) { 1148 byte[] bArray = (byte[])obj; 1149 for (int i = 0; i < bArray.length; i++) 1150 s += bArray[i] + " "; 1151 return s; 1152 } 1153 1154 if (obj instanceof int[]) { 1155 int[] iArray = (int[])obj; 1156 for (int i = 0; i < iArray.length; i++) 1157 s += iArray[i] + " " ; 1158 return s; 1159 } 1160 1161 if (obj instanceof short[]) { 1162 short[] sArray = (short[])obj; 1163 for (int i = 0; i < sArray.length; i++) 1164 s += sArray[i] + " " ; 1165 return s; 1166 } 1167 1168 return obj.toString(); 1169 1170 } 1171 1172 /** Checks that the provided <code>ImageWriter</code> can encode 1173 * the provided <code>ImageTypeSpecifier</code> or not. If not, an 1174 * <code>IIOException</code> will be thrown. 1175 * @param writer The provided <code>ImageWriter</code>. 1176 * @param type The image to be tested. 1177 * @throws IIOException If the writer cannot encoded the provided image. 1178 */ 1179 public static final void canEncodeImage(ImageWriter writer, 1180 ImageTypeSpecifier type) 1181 throws IIOException { 1182 ImageWriterSpi spi = writer.getOriginatingProvider(); 1183 1184 if(type != null && spi != null && !spi.canEncodeImage(type)) { 1185 throw new IIOException(I18N.getString("ImageUtil2")+" "+ 1186 writer.getClass().getName()); 1187 } 1188 } 1189 1190 /** Checks that the provided <code>ImageWriter</code> can encode 1191 * the provided <code>ColorModel</code> and <code>SampleModel</code>. 1192 * If not, an <code>IIOException</code> will be thrown. 1193 * @param writer The provided <code>ImageWriter</code>. 1194 * @param colorModel The provided <code>ColorModel</code>. 1195 * @param sampleModel The provided <code>SampleModel</code>. 1196 * @throws IIOException If the writer cannot encoded the provided image. 1197 */ 1198 public static final void canEncodeImage(ImageWriter writer, 1199 ColorModel colorModel, 1200 SampleModel sampleModel) 1201 throws IIOException { 1202 ImageTypeSpecifier type = null; 1203 if (colorModel != null && sampleModel != null) 1204 type = new ImageTypeSpecifier(colorModel, sampleModel); 1205 canEncodeImage(writer, type); 1206 } 1207 1208 /** 1209 * Returns whether the image has contiguous data across rows. 1210 */ 1211 public static final boolean imageIsContiguous(RenderedImage image) { 1212 SampleModel sm; 1213 if(image instanceof BufferedImage) { 1214 WritableRaster ras = ((BufferedImage)image).getRaster(); 1215 sm = ras.getSampleModel(); 1216 } else { 1217 sm = image.getSampleModel(); 1218 } 1219 1220 if (sm instanceof ComponentSampleModel) { 1221 // Ensure image rows samples are stored contiguously 1222 // in a single bank. 1223 ComponentSampleModel csm = (ComponentSampleModel)sm; 1224 1225 if (csm.getPixelStride() != csm.getNumBands()) { 1226 return false; 1227 } 1228 1229 int[] bandOffsets = csm.getBandOffsets(); 1230 for (int i = 0; i < bandOffsets.length; i++) { 1231 if (bandOffsets[i] != i) { 1232 return false; 1233 } 1234 } 1235 1236 int[] bankIndices = csm.getBankIndices(); 1237 for (int i = 0; i < bandOffsets.length; i++) { 1238 if (bankIndices[i] != 0) { 1239 return false; 1240 } 1241 } 1242 1243 return true; 1244 } 1245 1246 // Otherwise true if and only if it's a bilevel image with 1247 // a MultiPixelPackedSampleModel, 1 bit per pixel, and 1 bit 1248 // pixel stride. 1249 return ImageUtil.isBinary(sm); 1250 } 1251 1252 /** 1253 * Gets the destination image type. 1254 */ 1255 // NOTE: Code shamelessly copied from ImageReader.getDestination(). 1256 public static final ImageTypeSpecifier 1257 getDestinationType(ImageReadParam param, 1258 Iterator imageTypes) throws IIOException { 1259 1260 if (imageTypes == null || !imageTypes.hasNext()) { 1261 throw new IllegalArgumentException("imageTypes null or empty!"); 1262 } 1263 1264 ImageTypeSpecifier imageType = null; 1265 1266 // If param is non-null, use it 1267 if (param != null) { 1268 imageType = param.getDestinationType(); 1269 } 1270 1271 // No info from param, use fallback image type 1272 if (imageType == null) { 1273 Object o = imageTypes.next(); 1274 if (!(o instanceof ImageTypeSpecifier)) { 1275 throw new IllegalArgumentException 1276 ("Non-ImageTypeSpecifier retrieved from imageTypes!"); 1277 } 1278 imageType = (ImageTypeSpecifier)o; 1279 } else { 1280 boolean foundIt = false; 1281 while (imageTypes.hasNext()) { 1282 ImageTypeSpecifier type = 1283 (ImageTypeSpecifier)imageTypes.next(); 1284 if (type.equals(imageType)) { 1285 foundIt = true; 1286 break; 1287 } 1288 } 1289 1290 if (!foundIt) { 1291 throw new IIOException 1292 ("Destination type from ImageReadParam does not match!"); 1293 } 1294 } 1295 1296 return imageType; 1297 } 1298 1299 /** 1300 * Returns <code>true</code> if the given <code>ColorSpace</code> object 1301 * is an instance of <code>ICC_ColorSpace</code> but is not one of the 1302 * standard <code>ColorSpace</code>s returned by 1303 * <code>ColorSpace.getInstance()</code>. 1304 * 1305 * @param cs The <code>ColorSpace</code> to test. 1306 */ 1307 public static boolean isNonStandardICCColorSpace(ColorSpace cs) { 1308 boolean retval = false; 1309 1310 try { 1311 // Check the standard ColorSpaces in decreasing order of 1312 // likelihood except check CS_PYCC last as in some JREs 1313 // PYCC.pf used not to be installed. 1314 retval = 1315 (cs instanceof ICC_ColorSpace) && 1316 !(cs.isCS_sRGB() || 1317 cs.equals(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB)) || 1318 cs.equals(ColorSpace.getInstance(ColorSpace.CS_GRAY)) || 1319 cs.equals(ColorSpace.getInstance(ColorSpace.CS_CIEXYZ)) || 1320 cs.equals(ColorSpace.getInstance(ColorSpace.CS_PYCC))); 1321 } catch(IllegalArgumentException e) { 1322 // PYCC.pf not installed: ignore it - 'retval' is still 'false'. 1323 } 1324 1325 return retval; 1326 } 1327 1328 // Method to return JDK core ImageReaderSPI/ImageWriterSPI for a 1329 // given formatName. 1330 public static List getJDKImageReaderWriterSPI(ServiceRegistry registry, 1331 String formatName, 1332 boolean isReader) { 1333 1334 IIORegistry iioRegistry = (IIORegistry)registry; 1335 1336 Class spiClass; 1337 String descPart; 1338 if (isReader) { 1339 spiClass = ImageReaderSpi.class; 1340 descPart = " image reader"; 1341 } else { 1342 spiClass = ImageWriterSpi.class; 1343 descPart = " image writer"; 1344 } 1345 1346 Iterator iter = iioRegistry.getServiceProviders(spiClass, 1347 true); // useOrdering 1348 1349 String formatNames[]; 1350 ImageReaderWriterSpi provider; 1351 String desc = "standard " + formatName + descPart; 1352 String jiioPath = "com.github.jaiimageio.impl"; 1353 Locale locale = Locale.getDefault(); 1354 ArrayList list = new ArrayList(); 1355 while (iter.hasNext()) { 1356 provider = (ImageReaderWriterSpi)iter.next(); 1357 1358 // Look for JDK core ImageWriterSpi's 1359 if (provider.getVendorName().startsWith("Sun Microsystems") && 1360 desc.equalsIgnoreCase(provider.getDescription(locale)) && 1361 // not JAI Image I/O plugins 1362 !provider.getPluginClassName().startsWith(jiioPath)) { 1363 1364 // Get the formatNames supported by this Spi 1365 formatNames = provider.getFormatNames(); 1366 for (int i=0; i<formatNames.length; i++) { 1367 if (formatNames[i].equalsIgnoreCase(formatName)) { 1368 // Must be a JDK provided ImageReader/ImageWriter 1369 list.add(provider); 1370 break; 1371 } 1372 } 1373 } 1374 } 1375 1376 return list; 1377 } 1378 1379 public static void processOnRegistration(ServiceRegistry registry, 1380 Class category, 1381 String formatName, 1382 ImageReaderWriterSpi spi, 1383 int deregisterJvmVersion, 1384 int priorityJvmVersion) { 1385 1386 // Check which JVM we are running on 1387 String jvmVendor = System.getProperty("java.vendor"); 1388 String jvmVersionString = 1389 System.getProperty("java.specification.version"); 1390 int verIndex = jvmVersionString.indexOf("1."); 1391 // Skip the "1." part to get to the part of the version number that 1392 // actually changes from version to version 1393 // The assumption here is that "java.specification.version" is 1394 // always of the format "x.y" and not "x.y.z" since that is what has 1395 // been practically observed in all JDKs to-date, an examination of 1396 // the code returning this property bears this out. However this does 1397 // not guarantee that the format won't change in the future, 1398 // though that seems unlikely. 1399 jvmVersionString = jvmVersionString.substring(verIndex+2); 1400 1401 int jvmVersion = Integer.parseInt(jvmVersionString); 1402 1403 if (jvmVendor.equals("Sun Microsystems Inc.")) { 1404 1405 List list; 1406 if (spi instanceof ImageReaderSpi) 1407 list = getJDKImageReaderWriterSPI(registry, formatName, true); 1408 else 1409 list = getJDKImageReaderWriterSPI(registry, formatName, false); 1410 1411 if (jvmVersion >= deregisterJvmVersion && list.size() != 0) { 1412 // De-register JIIO's plug-in 1413 registry.deregisterServiceProvider(spi, category); 1414 } else { 1415 for (int i=0; i<list.size(); i++) { 1416 if (jvmVersion >= priorityJvmVersion) { 1417 // Set JIIO plug-in to lower priority 1418 registry.setOrdering(category, 1419 list.get(i), 1420 spi); 1421 } else { 1422 // Set JIIO plug-in to higher priority 1423 registry.setOrdering(category, 1424 spi, 1425 list.get(i)); 1426 } 1427 } 1428 } 1429 } 1430 } 1431 1432 public static int readMultiByteInteger(ImageInputStream iis) throws IOException { 1433 int value = iis.readByte(); 1434 int result = value & 0x7f; 1435 while((value & 0x80) == 0x80) { 1436 result <<= 7; 1437 value = iis.readByte(); 1438 result |= (value & 0x7f); 1439 } 1440 return result; 1441 } 1442}