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