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