1 /* 2 * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.imageio.plugins.common; 27 28 import java.awt.Point; 29 import java.awt.Rectangle; 30 import java.awt.Transparency; 31 import java.awt.color.ColorSpace; 32 import java.awt.image.BufferedImage; 33 import java.awt.image.ColorModel; 34 import java.awt.image.ComponentColorModel; 35 import java.awt.image.ComponentSampleModel; 36 import java.awt.image.DataBuffer; 37 import java.awt.image.DataBufferByte; 38 import java.awt.image.DataBufferInt; 39 import java.awt.image.DataBufferShort; 40 import java.awt.image.DataBufferUShort; 41 import java.awt.image.DirectColorModel; 42 import java.awt.image.IndexColorModel; 43 import java.awt.image.MultiPixelPackedSampleModel; 44 import java.awt.image.Raster; 45 import java.awt.image.RenderedImage; 46 import java.awt.image.SampleModel; 47 import java.awt.image.SinglePixelPackedSampleModel; 48 import java.awt.image.WritableRaster; 49 import java.util.Arrays; 50 51 //import javax.imageio.ImageTypeSpecifier; 52 53 import javax.imageio.IIOException; 54 import javax.imageio.IIOImage; 55 import javax.imageio.ImageTypeSpecifier; 56 import javax.imageio.ImageWriter; 57 import javax.imageio.spi.ImageWriterSpi; 58 59 public class ImageUtil { 60 /* XXX testing only 61 public static void main(String[] args) { 62 ImageTypeSpecifier bilevel = 63 ImageTypeSpecifier.createIndexed(new byte[] {(byte)0, (byte)255}, 64 new byte[] {(byte)0, (byte)255}, 65 new byte[] {(byte)0, (byte)255}, 66 null, 1, 67 DataBuffer.TYPE_BYTE); 68 ImageTypeSpecifier gray = 69 ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false); 70 ImageTypeSpecifier grayAlpha = 71 ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false, 72 false); 73 ImageTypeSpecifier rgb = 74 ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), 75 new int[] {0, 1, 2}, 76 DataBuffer.TYPE_BYTE, 77 false, 78 false); 79 ImageTypeSpecifier rgba = 80 ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), 81 new int[] {0, 1, 2, 3}, 82 DataBuffer.TYPE_BYTE, 83 true, 84 false); 85 ImageTypeSpecifier packed = 86 ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB), 87 0xff000000, 88 0x00ff0000, 89 0x0000ff00, 90 0x000000ff, 91 DataBuffer.TYPE_BYTE, 92 false); 93 94 SampleModel bandedSM = 95 new java.awt.image.BandedSampleModel(DataBuffer.TYPE_BYTE, 96 1, 1, 15); 97 98 System.out.println(createColorModel(bilevel.getSampleModel())); 99 System.out.println(createColorModel(gray.getSampleModel())); 100 System.out.println(createColorModel(grayAlpha.getSampleModel())); 101 System.out.println(createColorModel(rgb.getSampleModel())); 102 System.out.println(createColorModel(rgba.getSampleModel())); 103 System.out.println(createColorModel(packed.getSampleModel())); 104 System.out.println(createColorModel(bandedSM)); 105 } 106 */ 107 108 /** 109 * Creates a <code>ColorModel</code> that may be used with the 110 * specified <code>SampleModel</code>. If a suitable 111 * <code>ColorModel</code> cannot be found, this method returns 112 * <code>null</code>. 113 * 114 * <p> Suitable <code>ColorModel</code>s are guaranteed to exist 115 * for all instances of <code>ComponentSampleModel</code>. 116 * For 1- and 3- banded <code>SampleModel</code>s, the returned 117 * <code>ColorModel</code> will be opaque. For 2- and 4-banded 118 * <code>SampleModel</code>s, the output will use alpha transparency 119 * which is not premultiplied. 1- and 2-banded data will use a 120 * grayscale <code>ColorSpace</code>, and 3- and 4-banded data a sRGB 121 * <code>ColorSpace</code>. Data with 5 or more bands will have a 122 * <code>BogusColorSpace</code>.</p> 123 * 124 * <p>An instance of <code>DirectColorModel</code> will be created for 125 * instances of <code>SinglePixelPackedSampleModel</code> with no more 126 * than 4 bands.</p> 127 * 128 * <p>An instance of <code>IndexColorModel</code> will be created for 129 * instances of <code>MultiPixelPackedSampleModel</code>. The colormap 130 * will be a grayscale ramp with <code>1 << numberOfBits</code> 131 * entries ranging from zero to at most 255.</p> 132 * 133 * @return An instance of <code>ColorModel</code> that is suitable for 134 * the supplied <code>SampleModel</code>, or <code>null</code>. 135 * 136 * @throws IllegalArgumentException If <code>sampleModel</code> is 137 * <code>null</code>. 138 */ 139 public static final ColorModel createColorModel(SampleModel sampleModel) { 140 // Check the parameter. 141 if(sampleModel == null) { 142 throw new IllegalArgumentException("sampleModel == null!"); 143 } 144 145 // Get the data type. 146 int dataType = sampleModel.getDataType(); 147 148 // Check the data type 149 switch(dataType) { 150 case DataBuffer.TYPE_BYTE: 151 case DataBuffer.TYPE_USHORT: 152 case DataBuffer.TYPE_SHORT: 153 case DataBuffer.TYPE_INT: 154 case DataBuffer.TYPE_FLOAT: 155 case DataBuffer.TYPE_DOUBLE: 156 break; 157 default: 158 // Return null for other types. 159 return null; 160 } 161 162 // The return variable. 163 ColorModel colorModel = null; 164 165 // Get the sample size. 166 int[] sampleSize = sampleModel.getSampleSize(); 167 168 // Create a Component ColorModel. 169 if(sampleModel instanceof ComponentSampleModel) { 170 // Get the number of bands. 171 int numBands = sampleModel.getNumBands(); 172 173 // Determine the color space. 174 ColorSpace colorSpace = null; 175 if(numBands <= 2) { 176 colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY); 177 } else if(numBands <= 4) { 178 colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); 179 } else { 180 colorSpace = new BogusColorSpace(numBands); 181 } 182 183 boolean hasAlpha = (numBands == 2) || (numBands == 4); 184 boolean isAlphaPremultiplied = false; 185 int transparency = hasAlpha ? 186 Transparency.TRANSLUCENT : Transparency.OPAQUE; 187 188 colorModel = new ComponentColorModel(colorSpace, 189 sampleSize, 190 hasAlpha, 191 isAlphaPremultiplied, 192 transparency, 193 dataType); 194 } else if (sampleModel.getNumBands() <= 4 && 195 sampleModel instanceof SinglePixelPackedSampleModel) { 196 SinglePixelPackedSampleModel sppsm = 197 (SinglePixelPackedSampleModel)sampleModel; 198 199 int[] bitMasks = sppsm.getBitMasks(); 200 int rmask = 0; 201 int gmask = 0; 202 int bmask = 0; 203 int amask = 0; 204 205 int numBands = bitMasks.length; 206 if (numBands <= 2) { 207 rmask = gmask = bmask = bitMasks[0]; 208 if (numBands == 2) { 209 amask = bitMasks[1]; 210 } 211 } else { 212 rmask = bitMasks[0]; 213 gmask = bitMasks[1]; 214 bmask = bitMasks[2]; 215 if (numBands == 4) { 216 amask = bitMasks[3]; 217 } 218 } 219 220 int bits = 0; 221 for (int i = 0; i < sampleSize.length; i++) { 222 bits += sampleSize[i]; 223 } 224 225 return new DirectColorModel(bits, rmask, gmask, bmask, amask); 226 227 } else if(sampleModel instanceof MultiPixelPackedSampleModel) { 228 // Load the colormap with a ramp. 229 int bitsPerSample = sampleSize[0]; 230 int numEntries = 1 << bitsPerSample; 231 byte[] map = new byte[numEntries]; 232 for (int i = 0; i < numEntries; i++) { 233 map[i] = (byte)(i*255/(numEntries - 1)); 234 } 235 236 colorModel = new IndexColorModel(bitsPerSample, numEntries, 237 map, map, map); 238 239 } 240 241 return colorModel; 242 } 243 244 /** 245 * For the case of binary data (<code>isBinary()</code> returns 246 * <code>true</code>), return the binary data as a packed byte array. 247 * The data will be packed as eight bits per byte with no bit offset, 248 * i.e., the first bit in each image line will be the left-most of the 249 * first byte of the line. The line stride in bytes will be 250 * <code>(int)((getWidth()+7)/8)</code>. The length of the returned 251 * array will be the line stride multiplied by <code>getHeight()</code> 252 * 253 * @return the binary data as a packed array of bytes with zero offset 254 * of <code>null</code> if the data are not binary. 255 * @throws IllegalArgumentException if <code>isBinary()</code> returns 256 * <code>false</code> with the <code>SampleModel</code> of the 257 * supplied <code>Raster</code> as argument. 258 */ 259 public static byte[] getPackedBinaryData(Raster raster, 260 Rectangle rect) { 261 SampleModel sm = raster.getSampleModel(); 262 if(!isBinary(sm)) { 263 throw new IllegalArgumentException(I18N.getString("ImageUtil0")); 264 } 265 266 int rectX = rect.x; 267 int rectY = rect.y; 268 int rectWidth = rect.width; 269 int rectHeight = rect.height; 270 271 DataBuffer dataBuffer = raster.getDataBuffer(); 272 273 int dx = rectX - raster.getSampleModelTranslateX(); 274 int dy = rectY - raster.getSampleModelTranslateY(); 275 276 MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; 277 int lineStride = mpp.getScanlineStride(); 278 int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); 279 int bitOffset = mpp.getBitOffset(dx); 280 281 int numBytesPerRow = (rectWidth + 7)/8; 282 if(dataBuffer instanceof DataBufferByte && 283 eltOffset == 0 && bitOffset == 0 && 284 numBytesPerRow == lineStride && 285 ((DataBufferByte)dataBuffer).getData().length == 286 numBytesPerRow*rectHeight) { 287 return ((DataBufferByte)dataBuffer).getData(); 288 } 289 290 byte[] binaryDataArray = new byte[numBytesPerRow*rectHeight]; 291 292 int b = 0; 293 294 if(bitOffset == 0) { 295 if(dataBuffer instanceof DataBufferByte) { 296 byte[] data = ((DataBufferByte)dataBuffer).getData(); 297 int stride = numBytesPerRow; 298 int offset = 0; 299 for(int y = 0; y < rectHeight; y++) { 300 System.arraycopy(data, eltOffset, 301 binaryDataArray, offset, 302 stride); 303 offset += stride; 304 eltOffset += lineStride; 305 } 306 } else if(dataBuffer instanceof DataBufferShort || 307 dataBuffer instanceof DataBufferUShort) { 308 short[] data = dataBuffer instanceof DataBufferShort ? 309 ((DataBufferShort)dataBuffer).getData() : 310 ((DataBufferUShort)dataBuffer).getData(); 311 312 for(int y = 0; y < rectHeight; y++) { 313 int xRemaining = rectWidth; 314 int i = eltOffset; 315 while(xRemaining > 8) { 316 short datum = data[i++]; 317 binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF); 318 binaryDataArray[b++] = (byte)(datum & 0xFF); 319 xRemaining -= 16; 320 } 321 if(xRemaining > 0) { 322 binaryDataArray[b++] = (byte)((data[i] >>> 8) & 0XFF); 323 } 324 eltOffset += lineStride; 325 } 326 } else if(dataBuffer instanceof DataBufferInt) { 327 int[] data = ((DataBufferInt)dataBuffer).getData(); 328 329 for(int y = 0; y < rectHeight; y++) { 330 int xRemaining = rectWidth; 331 int i = eltOffset; 332 while(xRemaining > 24) { 333 int datum = data[i++]; 334 binaryDataArray[b++] = (byte)((datum >>> 24) & 0xFF); 335 binaryDataArray[b++] = (byte)((datum >>> 16) & 0xFF); 336 binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF); 337 binaryDataArray[b++] = (byte)(datum & 0xFF); 338 xRemaining -= 32; 339 } 340 int shift = 24; 341 while(xRemaining > 0) { 342 binaryDataArray[b++] = 343 (byte)((data[i] >>> shift) & 0xFF); 344 shift -= 8; 345 xRemaining -= 8; 346 } 347 eltOffset += lineStride; 348 } 349 } 350 } else { // bitOffset != 0 351 if(dataBuffer instanceof DataBufferByte) { 352 byte[] data = ((DataBufferByte)dataBuffer).getData(); 353 354 if((bitOffset & 7) == 0) { 355 int stride = numBytesPerRow; 356 int offset = 0; 357 for(int y = 0; y < rectHeight; y++) { 358 System.arraycopy(data, eltOffset, 359 binaryDataArray, offset, 360 stride); 361 offset += stride; 362 eltOffset += lineStride; 363 } 364 } else { // bitOffset % 8 != 0 365 int leftShift = bitOffset & 7; 366 int rightShift = 8 - leftShift; 367 for(int y = 0; y < rectHeight; y++) { 368 int i = eltOffset; 369 int xRemaining = rectWidth; 370 while(xRemaining > 0) { 371 if(xRemaining > rightShift) { 372 binaryDataArray[b++] = 373 (byte)(((data[i++]&0xFF) << leftShift) | 374 ((data[i]&0xFF) >>> rightShift)); 375 } else { 376 binaryDataArray[b++] = 377 (byte)((data[i]&0xFF) << leftShift); 378 } 379 xRemaining -= 8; 380 } 381 eltOffset += lineStride; 382 } 383 } 384 } else if(dataBuffer instanceof DataBufferShort || 385 dataBuffer instanceof DataBufferUShort) { 386 short[] data = dataBuffer instanceof DataBufferShort ? 387 ((DataBufferShort)dataBuffer).getData() : 388 ((DataBufferUShort)dataBuffer).getData(); 389 390 for(int y = 0; y < rectHeight; y++) { 391 int bOffset = bitOffset; 392 for(int x = 0; x < rectWidth; x += 8, bOffset += 8) { 393 int i = eltOffset + bOffset/16; 394 int mod = bOffset % 16; 395 int left = data[i] & 0xFFFF; 396 if(mod <= 8) { 397 binaryDataArray[b++] = (byte)(left >>> (8 - mod)); 398 } else { 399 int delta = mod - 8; 400 int right = data[i+1] & 0xFFFF; 401 binaryDataArray[b++] = 402 (byte)((left << delta) | 403 (right >>> (16 - delta))); 404 } 405 } 406 eltOffset += lineStride; 407 } 408 } else if(dataBuffer instanceof DataBufferInt) { 409 int[] data = ((DataBufferInt)dataBuffer).getData(); 410 411 for(int y = 0; y < rectHeight; y++) { 412 int bOffset = bitOffset; 413 for(int x = 0; x < rectWidth; x += 8, bOffset += 8) { 414 int i = eltOffset + bOffset/32; 415 int mod = bOffset % 32; 416 int left = data[i]; 417 if(mod <= 24) { 418 binaryDataArray[b++] = 419 (byte)(left >>> (24 - mod)); 420 } else { 421 int delta = mod - 24; 422 int right = data[i+1]; 423 binaryDataArray[b++] = 424 (byte)((left << delta) | 425 (right >>> (32 - delta))); 426 } 427 } 428 eltOffset += lineStride; 429 } 430 } 431 } 432 433 return binaryDataArray; 434 } 435 436 /** 437 * Returns the binary data unpacked into an array of bytes. 438 * The line stride will be the width of the <code>Raster</code>. 439 * 440 * @throws IllegalArgumentException if <code>isBinary()</code> returns 441 * <code>false</code> with the <code>SampleModel</code> of the 442 * supplied <code>Raster</code> as argument. 443 */ 444 public static byte[] getUnpackedBinaryData(Raster raster, 445 Rectangle rect) { 446 SampleModel sm = raster.getSampleModel(); 447 if(!isBinary(sm)) { 448 throw new IllegalArgumentException(I18N.getString("ImageUtil0")); 449 } 450 451 int rectX = rect.x; 452 int rectY = rect.y; 453 int rectWidth = rect.width; 454 int rectHeight = rect.height; 455 456 DataBuffer dataBuffer = raster.getDataBuffer(); 457 458 int dx = rectX - raster.getSampleModelTranslateX(); 459 int dy = rectY - raster.getSampleModelTranslateY(); 460 461 MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; 462 int lineStride = mpp.getScanlineStride(); 463 int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); 464 int bitOffset = mpp.getBitOffset(dx); 465 466 byte[] bdata = new byte[rectWidth*rectHeight]; 467 int maxY = rectY + rectHeight; 468 int maxX = rectX + rectWidth; 469 int k = 0; 470 471 if(dataBuffer instanceof DataBufferByte) { 472 byte[] data = ((DataBufferByte)dataBuffer).getData(); 473 for(int y = rectY; y < maxY; y++) { 474 int bOffset = eltOffset*8 + bitOffset; 475 for(int x = rectX; x < maxX; x++) { 476 byte b = data[bOffset/8]; 477 bdata[k++] = 478 (byte)((b >>> (7 - bOffset & 7)) & 0x0000001); 479 bOffset++; 480 } 481 eltOffset += lineStride; 482 } 483 } else if(dataBuffer instanceof DataBufferShort || 484 dataBuffer instanceof DataBufferUShort) { 485 short[] data = dataBuffer instanceof DataBufferShort ? 486 ((DataBufferShort)dataBuffer).getData() : 487 ((DataBufferUShort)dataBuffer).getData(); 488 for(int y = rectY; y < maxY; y++) { 489 int bOffset = eltOffset*16 + bitOffset; 490 for(int x = rectX; x < maxX; x++) { 491 short s = data[bOffset/16]; 492 bdata[k++] = 493 (byte)((s >>> (15 - bOffset % 16)) & 494 0x0000001); 495 bOffset++; 496 } 497 eltOffset += lineStride; 498 } 499 } else if(dataBuffer instanceof DataBufferInt) { 500 int[] data = ((DataBufferInt)dataBuffer).getData(); 501 for(int y = rectY; y < maxY; y++) { 502 int bOffset = eltOffset*32 + bitOffset; 503 for(int x = rectX; x < maxX; x++) { 504 int i = data[bOffset/32]; 505 bdata[k++] = 506 (byte)((i >>> (31 - bOffset % 32)) & 507 0x0000001); 508 bOffset++; 509 } 510 eltOffset += lineStride; 511 } 512 } 513 514 return bdata; 515 } 516 517 /** 518 * Sets the supplied <code>Raster</code>'s data from an array 519 * of packed binary data of the form returned by 520 * <code>getPackedBinaryData()</code>. 521 * 522 * @throws IllegalArgumentException if <code>isBinary()</code> returns 523 * <code>false</code> with the <code>SampleModel</code> of the 524 * supplied <code>Raster</code> as argument. 525 */ 526 public static void setPackedBinaryData(byte[] binaryDataArray, 527 WritableRaster raster, 528 Rectangle rect) { 529 SampleModel sm = raster.getSampleModel(); 530 if(!isBinary(sm)) { 531 throw new IllegalArgumentException(I18N.getString("ImageUtil0")); 532 } 533 534 int rectX = rect.x; 535 int rectY = rect.y; 536 int rectWidth = rect.width; 537 int rectHeight = rect.height; 538 539 DataBuffer dataBuffer = raster.getDataBuffer(); 540 541 int dx = rectX - raster.getSampleModelTranslateX(); 542 int dy = rectY - raster.getSampleModelTranslateY(); 543 544 MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; 545 int lineStride = mpp.getScanlineStride(); 546 int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); 547 int bitOffset = mpp.getBitOffset(dx); 548 549 int b = 0; 550 551 if(bitOffset == 0) { 552 if(dataBuffer instanceof DataBufferByte) { 553 byte[] data = ((DataBufferByte)dataBuffer).getData(); 554 if(data == binaryDataArray) { 555 // Optimal case: simply return. 556 return; 557 } 558 int stride = (rectWidth + 7)/8; 559 int offset = 0; 560 for(int y = 0; y < rectHeight; y++) { 561 System.arraycopy(binaryDataArray, offset, 562 data, eltOffset, 563 stride); 564 offset += stride; 565 eltOffset += lineStride; 566 } 567 } else if(dataBuffer instanceof DataBufferShort || 568 dataBuffer instanceof DataBufferUShort) { 569 short[] data = dataBuffer instanceof DataBufferShort ? 570 ((DataBufferShort)dataBuffer).getData() : 571 ((DataBufferUShort)dataBuffer).getData(); 572 573 for(int y = 0; y < rectHeight; y++) { 574 int xRemaining = rectWidth; 575 int i = eltOffset; 576 while(xRemaining > 8) { 577 data[i++] = 578 (short)(((binaryDataArray[b++] & 0xFF) << 8) | 579 (binaryDataArray[b++] & 0xFF)); 580 xRemaining -= 16; 581 } 582 if(xRemaining > 0) { 583 data[i++] = 584 (short)((binaryDataArray[b++] & 0xFF) << 8); 585 } 586 eltOffset += lineStride; 587 } 588 } else if(dataBuffer instanceof DataBufferInt) { 589 int[] data = ((DataBufferInt)dataBuffer).getData(); 590 591 for(int y = 0; y < rectHeight; y++) { 592 int xRemaining = rectWidth; 593 int i = eltOffset; 594 while(xRemaining > 24) { 595 data[i++] = 596 (((binaryDataArray[b++] & 0xFF) << 24) | 597 ((binaryDataArray[b++] & 0xFF) << 16) | 598 ((binaryDataArray[b++] & 0xFF) << 8) | 599 (binaryDataArray[b++] & 0xFF)); 600 xRemaining -= 32; 601 } 602 int shift = 24; 603 while(xRemaining > 0) { 604 data[i] |= ((binaryDataArray[b++] & 0xFF) << shift); 605 shift -= 8; 606 xRemaining -= 8; 607 } 608 eltOffset += lineStride; 609 } 610 } 611 } else { // bitOffset != 0 612 int stride = (rectWidth + 7)/8; 613 int offset = 0; 614 if(dataBuffer instanceof DataBufferByte) { 615 byte[] data = ((DataBufferByte)dataBuffer).getData(); 616 617 if((bitOffset & 7) == 0) { 618 for(int y = 0; y < rectHeight; y++) { 619 System.arraycopy(binaryDataArray, offset, 620 data, eltOffset, 621 stride); 622 offset += stride; 623 eltOffset += lineStride; 624 } 625 } else { // bitOffset % 8 != 0 626 int rightShift = bitOffset & 7; 627 int leftShift = 8 - rightShift; 628 int leftShift8 = 8 + leftShift; 629 int mask = (byte)(255<<leftShift); 630 int mask1 = (byte)~mask; 631 632 for(int y = 0; y < rectHeight; y++) { 633 int i = eltOffset; 634 int xRemaining = rectWidth; 635 while(xRemaining > 0) { 636 byte datum = binaryDataArray[b++]; 637 638 if (xRemaining > leftShift8) { 639 // when all the bits in this BYTE will be set 640 // into the data buffer. 641 data[i] = (byte)((data[i] & mask ) | 642 ((datum&0xFF) >>> rightShift)); 643 data[++i] = (byte)((datum & 0xFF) << leftShift); 644 } else if (xRemaining > leftShift) { 645 // All the "leftShift" high bits will be set 646 // into the data buffer. But not all the 647 // "rightShift" low bits will be set. 648 data[i] = (byte)((data[i] & mask ) | 649 ((datum&0xFF) >>> rightShift)); 650 i++; 651 data[i] = 652 (byte)((data[i] & mask1) | ((datum & 0xFF) << leftShift)); 653 } 654 else { 655 // Less than "leftShift" high bits will be set. 656 int remainMask = (1 << leftShift - xRemaining) - 1; 657 data[i] = 658 (byte)((data[i] & (mask | remainMask)) | 659 (datum&0xFF) >>> rightShift & ~remainMask); 660 } 661 xRemaining -= 8; 662 } 663 eltOffset += lineStride; 664 } 665 } 666 } else if(dataBuffer instanceof DataBufferShort || 667 dataBuffer instanceof DataBufferUShort) { 668 short[] data = dataBuffer instanceof DataBufferShort ? 669 ((DataBufferShort)dataBuffer).getData() : 670 ((DataBufferUShort)dataBuffer).getData(); 671 672 int rightShift = bitOffset & 7; 673 int leftShift = 8 - rightShift; 674 int leftShift16 = 16 + leftShift; 675 int mask = (short)(~(255 << leftShift)); 676 int mask1 = (short)(65535 << leftShift); 677 int mask2 = (short)~mask1; 678 679 for(int y = 0; y < rectHeight; y++) { 680 int bOffset = bitOffset; 681 int xRemaining = rectWidth; 682 for(int x = 0; x < rectWidth; 683 x += 8, bOffset += 8, xRemaining -= 8) { 684 int i = eltOffset + (bOffset >> 4); 685 int mod = bOffset & 15; 686 int datum = binaryDataArray[b++] & 0xFF; 687 if(mod <= 8) { 688 // This BYTE is set into one SHORT 689 if (xRemaining < 8) { 690 // Mask the bits to be set. 691 datum &= 255 << 8 - xRemaining; 692 } 693 data[i] = (short)((data[i] & mask) | (datum << leftShift)); 694 } else if (xRemaining > leftShift16) { 695 // This BYTE will be set into two SHORTs 696 data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF)); 697 data[++i] = 698 (short)((datum << leftShift)&0xFFFF); 699 } else if (xRemaining > leftShift) { 700 // This BYTE will be set into two SHORTs; 701 // But not all the low bits will be set into SHORT 702 data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF)); 703 i++; 704 data[i] = 705 (short)((data[i] & mask2) | ((datum << leftShift)&0xFFFF)); 706 } else { 707 // Only some of the high bits will be set into 708 // SHORTs 709 int remainMask = (1 << leftShift - xRemaining) - 1; 710 data[i] = (short)((data[i] & (mask1 | remainMask)) | 711 ((datum >>> rightShift)&0xFFFF & ~remainMask)); 712 } 713 } 714 eltOffset += lineStride; 715 } 716 } else if(dataBuffer instanceof DataBufferInt) { 717 int[] data = ((DataBufferInt)dataBuffer).getData(); 718 int rightShift = bitOffset & 7; 719 int leftShift = 8 - rightShift; 720 int leftShift32 = 32 + leftShift; 721 int mask = 0xFFFFFFFF << leftShift; 722 int mask1 = ~mask; 723 724 for(int y = 0; y < rectHeight; y++) { 725 int bOffset = bitOffset; 726 int xRemaining = rectWidth; 727 for(int x = 0; x < rectWidth; 728 x += 8, bOffset += 8, xRemaining -= 8) { 729 int i = eltOffset + (bOffset >> 5); 730 int mod = bOffset & 31; 731 int datum = binaryDataArray[b++] & 0xFF; 732 if(mod <= 24) { 733 // This BYTE is set into one INT 734 int shift = 24 - mod; 735 if (xRemaining < 8) { 736 // Mask the bits to be set. 737 datum &= 255 << 8 - xRemaining; 738 } 739 data[i] = (data[i] & (~(255 << shift))) | (datum << shift); 740 } else if (xRemaining > leftShift32) { 741 // All the bits of this BYTE will be set into two INTs 742 data[i] = (data[i] & mask) | (datum >>> rightShift); 743 data[++i] = datum << leftShift; 744 } else if (xRemaining > leftShift) { 745 // This BYTE will be set into two INTs; 746 // But not all the low bits will be set into INT 747 data[i] = (data[i] & mask) | (datum >>> rightShift); 748 i++; 749 data[i] = (data[i] & mask1) | (datum << leftShift); 750 } else { 751 // Only some of the high bits will be set into INT 752 int remainMask = (1 << leftShift - xRemaining) - 1; 753 data[i] = (data[i] & (mask | remainMask)) | 754 (datum >>> rightShift & ~remainMask); 755 } 756 } 757 eltOffset += lineStride; 758 } 759 } 760 } 761 } 762 763 /** 764 * Copies data into the packed array of the <code>Raster</code> 765 * from an array of unpacked data of the form returned by 766 * <code>getUnpackedBinaryData()</code>. 767 * 768 * <p> If the data are binary, then the target bit will be set if 769 * and only if the corresponding byte is non-zero. 770 * 771 * @throws IllegalArgumentException if <code>isBinary()</code> returns 772 * <code>false</code> with the <code>SampleModel</code> of the 773 * supplied <code>Raster</code> as argument. 774 */ 775 public static void setUnpackedBinaryData(byte[] bdata, 776 WritableRaster raster, 777 Rectangle rect) { 778 SampleModel sm = raster.getSampleModel(); 779 if(!isBinary(sm)) { 780 throw new IllegalArgumentException(I18N.getString("ImageUtil0")); 781 } 782 783 int rectX = rect.x; 784 int rectY = rect.y; 785 int rectWidth = rect.width; 786 int rectHeight = rect.height; 787 788 DataBuffer dataBuffer = raster.getDataBuffer(); 789 790 int dx = rectX - raster.getSampleModelTranslateX(); 791 int dy = rectY - raster.getSampleModelTranslateY(); 792 793 MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; 794 int lineStride = mpp.getScanlineStride(); 795 int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); 796 int bitOffset = mpp.getBitOffset(dx); 797 798 int k = 0; 799 800 if(dataBuffer instanceof DataBufferByte) { 801 byte[] data = ((DataBufferByte)dataBuffer).getData(); 802 for(int y = 0; y < rectHeight; y++) { 803 int bOffset = eltOffset*8 + bitOffset; 804 for(int x = 0; x < rectWidth; x++) { 805 if(bdata[k++] != (byte)0) { 806 data[bOffset/8] |= 807 (byte)(0x00000001 << (7 - bOffset & 7)); 808 } 809 bOffset++; 810 } 811 eltOffset += lineStride; 812 } 813 } else if(dataBuffer instanceof DataBufferShort || 814 dataBuffer instanceof DataBufferUShort) { 815 short[] data = dataBuffer instanceof DataBufferShort ? 816 ((DataBufferShort)dataBuffer).getData() : 817 ((DataBufferUShort)dataBuffer).getData(); 818 for(int y = 0; y < rectHeight; y++) { 819 int bOffset = eltOffset*16 + bitOffset; 820 for(int x = 0; x < rectWidth; x++) { 821 if(bdata[k++] != (byte)0) { 822 data[bOffset/16] |= 823 (short)(0x00000001 << 824 (15 - bOffset % 16)); 825 } 826 bOffset++; 827 } 828 eltOffset += lineStride; 829 } 830 } else if(dataBuffer instanceof DataBufferInt) { 831 int[] data = ((DataBufferInt)dataBuffer).getData(); 832 for(int y = 0; y < rectHeight; y++) { 833 int bOffset = eltOffset*32 + bitOffset; 834 for(int x = 0; x < rectWidth; x++) { 835 if(bdata[k++] != (byte)0) { 836 data[bOffset/32] |= 837 (0x00000001 << (31 - bOffset % 32)); 838 } 839 bOffset++; 840 } 841 eltOffset += lineStride; 842 } 843 } 844 } 845 846 public static boolean isBinary(SampleModel sm) { 847 return sm instanceof MultiPixelPackedSampleModel && 848 ((MultiPixelPackedSampleModel)sm).getPixelBitStride() == 1 && 849 sm.getNumBands() == 1; 850 } 851 852 public static ColorModel createColorModel(ColorSpace colorSpace, 853 SampleModel sampleModel) { 854 ColorModel colorModel = null; 855 856 if(sampleModel == null) { 857 throw new IllegalArgumentException(I18N.getString("ImageUtil1")); 858 } 859 860 int numBands = sampleModel.getNumBands(); 861 if (numBands < 1 || numBands > 4) { 862 return null; 863 } 864 865 int dataType = sampleModel.getDataType(); 866 if (sampleModel instanceof ComponentSampleModel) { 867 if (dataType < DataBuffer.TYPE_BYTE || 868 //dataType == DataBuffer.TYPE_SHORT || 869 dataType > DataBuffer.TYPE_DOUBLE) { 870 return null; 871 } 872 873 if (colorSpace == null) 874 colorSpace = 875 numBands <= 2 ? 876 ColorSpace.getInstance(ColorSpace.CS_GRAY) : 877 ColorSpace.getInstance(ColorSpace.CS_sRGB); 878 879 boolean useAlpha = (numBands == 2) || (numBands == 4); 880 int transparency = useAlpha ? 881 Transparency.TRANSLUCENT : Transparency.OPAQUE; 882 883 boolean premultiplied = false; 884 885 int dataTypeSize = DataBuffer.getDataTypeSize(dataType); 886 int[] bits = new int[numBands]; 887 for (int i = 0; i < numBands; i++) { 888 bits[i] = dataTypeSize; 889 } 890 891 colorModel = new ComponentColorModel(colorSpace, 892 bits, 893 useAlpha, 894 premultiplied, 895 transparency, 896 dataType); 897 } else if (sampleModel instanceof SinglePixelPackedSampleModel) { 898 SinglePixelPackedSampleModel sppsm = 899 (SinglePixelPackedSampleModel)sampleModel; 900 901 int[] bitMasks = sppsm.getBitMasks(); 902 int rmask = 0; 903 int gmask = 0; 904 int bmask = 0; 905 int amask = 0; 906 907 numBands = bitMasks.length; 908 if (numBands <= 2) { 909 rmask = gmask = bmask = bitMasks[0]; 910 if (numBands == 2) { 911 amask = bitMasks[1]; 912 } 913 } else { 914 rmask = bitMasks[0]; 915 gmask = bitMasks[1]; 916 bmask = bitMasks[2]; 917 if (numBands == 4) { 918 amask = bitMasks[3]; 919 } 920 } 921 922 int[] sampleSize = sppsm.getSampleSize(); 923 int bits = 0; 924 for (int i = 0; i < sampleSize.length; i++) { 925 bits += sampleSize[i]; 926 } 927 928 if (colorSpace == null) 929 colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); 930 931 colorModel = 932 new DirectColorModel(colorSpace, 933 bits, rmask, gmask, bmask, amask, 934 false, 935 sampleModel.getDataType()); 936 } else if (sampleModel instanceof MultiPixelPackedSampleModel) { 937 int bits = 938 ((MultiPixelPackedSampleModel)sampleModel).getPixelBitStride(); 939 int size = 1 << bits; 940 byte[] comp = new byte[size]; 941 942 for (int i = 0; i < size; i++) 943 comp[i] = (byte)(255 * i / (size - 1)); 944 945 colorModel = new IndexColorModel(bits, size, comp, comp, comp); 946 } 947 948 return colorModel; 949 } 950 951 public static int getElementSize(SampleModel sm) { 952 int elementSize = DataBuffer.getDataTypeSize(sm.getDataType()); 953 954 if (sm instanceof MultiPixelPackedSampleModel) { 955 MultiPixelPackedSampleModel mppsm = 956 (MultiPixelPackedSampleModel)sm; 957 return mppsm.getSampleSize(0) * mppsm.getNumBands(); 958 } else if (sm instanceof ComponentSampleModel) { 959 return sm.getNumBands() * elementSize; 960 } else if (sm instanceof SinglePixelPackedSampleModel) { 961 return elementSize; 962 } 963 964 return elementSize * sm.getNumBands(); 965 966 } 967 968 public static long getTileSize(SampleModel sm) { 969 int elementSize = DataBuffer.getDataTypeSize(sm.getDataType()); 970 971 if (sm instanceof MultiPixelPackedSampleModel) { 972 MultiPixelPackedSampleModel mppsm = 973 (MultiPixelPackedSampleModel)sm; 974 return (mppsm.getScanlineStride() * mppsm.getHeight() + 975 (mppsm.getDataBitOffset() + elementSize -1) / elementSize) * 976 ((elementSize + 7) / 8); 977 } else if (sm instanceof ComponentSampleModel) { 978 ComponentSampleModel csm = (ComponentSampleModel)sm; 979 int[] bandOffsets = csm.getBandOffsets(); 980 int maxBandOff = bandOffsets[0]; 981 for (int i=1; i<bandOffsets.length; i++) 982 maxBandOff = Math.max(maxBandOff, bandOffsets[i]); 983 984 long size = 0; 985 int pixelStride = csm.getPixelStride(); 986 int scanlineStride = csm.getScanlineStride(); 987 if (maxBandOff >= 0) 988 size += maxBandOff + 1; 989 if (pixelStride > 0) 990 size += pixelStride * (sm.getWidth() - 1); 991 if (scanlineStride > 0) 992 size += scanlineStride * (sm.getHeight() - 1); 993 994 int[] bankIndices = csm.getBankIndices(); 995 maxBandOff = bankIndices[0]; 996 for (int i=1; i<bankIndices.length; i++) 997 maxBandOff = Math.max(maxBandOff, bankIndices[i]); 998 return size * (maxBandOff + 1) * ((elementSize + 7) / 8); 999 } else if (sm instanceof SinglePixelPackedSampleModel) { 1000 SinglePixelPackedSampleModel sppsm = 1001 (SinglePixelPackedSampleModel)sm; 1002 long size = sppsm.getScanlineStride() * (sppsm.getHeight() - 1) + 1003 sppsm.getWidth(); 1004 return size * ((elementSize + 7) / 8); 1005 } 1006 1007 return 0; 1008 } 1009 1010 public static long getBandSize(SampleModel sm) { 1011 int elementSize = DataBuffer.getDataTypeSize(sm.getDataType()); 1012 1013 if (sm instanceof ComponentSampleModel) { 1014 ComponentSampleModel csm = (ComponentSampleModel)sm; 1015 int pixelStride = csm.getPixelStride(); 1016 int scanlineStride = csm.getScanlineStride(); 1017 long size = Math.min(pixelStride, scanlineStride); 1018 1019 if (pixelStride > 0) 1020 size += pixelStride * (sm.getWidth() - 1); 1021 if (scanlineStride > 0) 1022 size += scanlineStride * (sm.getHeight() - 1); 1023 return size * ((elementSize + 7) / 8); 1024 } else 1025 return getTileSize(sm); 1026 } 1027 /** 1028 * Tests whether the color indices represent a gray-scale image. 1029 * 1030 * @param r The red channel color indices. 1031 * @param g The green channel color indices. 1032 * @param b The blue channel color indices. 1033 * @return If all the indices have 256 entries, and are identical mappings, 1034 * return <code>true</code>; otherwise, return <code>false</code>. 1035 */ 1036 public static boolean isIndicesForGrayscale(byte[] r, byte[] g, byte[] b) { 1037 if (r.length != g.length || r.length != b.length) 1038 return false; 1039 1040 int size = r.length; 1041 1042 if (size != 256) 1043 return false; 1044 1045 for (int i = 0; i < size; i++) { 1046 byte temp = (byte) i; 1047 1048 if (r[i] != temp || g[i] != temp || b[i] != temp) 1049 return false; 1050 } 1051 1052 return true; 1053 } 1054 1055 /** Converts the provided object to <code>String</code> */ 1056 public static String convertObjectToString(Object obj) { 1057 if (obj == null) 1058 return ""; 1059 1060 String s = ""; 1061 if (obj instanceof byte[]) { 1062 byte[] bArray = (byte[])obj; 1063 for (int i = 0; i < bArray.length; i++) 1064 s += bArray[i] + " "; 1065 return s; 1066 } 1067 1068 if (obj instanceof int[]) { 1069 int[] iArray = (int[])obj; 1070 for (int i = 0; i < iArray.length; i++) 1071 s += iArray[i] + " " ; 1072 return s; 1073 } 1074 1075 if (obj instanceof short[]) { 1076 short[] sArray = (short[])obj; 1077 for (int i = 0; i < sArray.length; i++) 1078 s += sArray[i] + " " ; 1079 return s; 1080 } 1081 1082 return obj.toString(); 1083 1084 } 1085 1086 /** Checks that the provided <code>ImageWriter</code> can encode 1087 * the provided <code>ImageTypeSpecifier</code> or not. If not, an 1088 * <code>IIOException</code> will be thrown. 1089 * @param writer The provided <code>ImageWriter</code>. 1090 * @param type The image to be tested. 1091 * @throws IIOException If the writer cannot encoded the provided image. 1092 */ 1093 public static final void canEncodeImage(ImageWriter writer, 1094 ImageTypeSpecifier type) 1095 throws IIOException { 1096 ImageWriterSpi spi = writer.getOriginatingProvider(); 1097 1098 if(type != null && spi != null && !spi.canEncodeImage(type)) { 1099 throw new IIOException(I18N.getString("ImageUtil2")+" "+ 1100 writer.getClass().getName()); 1101 } 1102 } 1103 1104 /** Checks that the provided <code>ImageWriter</code> can encode 1105 * the provided <code>ColorModel</code> and <code>SampleModel</code>. 1106 * If not, an <code>IIOException</code> will be thrown. 1107 * @param writer The provided <code>ImageWriter</code>. 1108 * @param colorModel The provided <code>ColorModel</code>. 1109 * @param sampleModel The provided <code>SampleModel</code>. 1110 * @throws IIOException If the writer cannot encoded the provided image. 1111 */ 1112 public static final void canEncodeImage(ImageWriter writer, 1113 ColorModel colorModel, 1114 SampleModel sampleModel) 1115 throws IIOException { 1116 ImageTypeSpecifier type = null; 1117 if (colorModel != null && sampleModel != null) 1118 type = new ImageTypeSpecifier(colorModel, sampleModel); 1119 canEncodeImage(writer, type); 1120 } 1121 1122 /** 1123 * Returns whether the image has contiguous data across rows. 1124 */ 1125 public static final boolean imageIsContiguous(RenderedImage image) { 1126 SampleModel sm; 1127 if(image instanceof BufferedImage) { 1128 WritableRaster ras = ((BufferedImage)image).getRaster(); 1129 sm = ras.getSampleModel(); 1130 } else { 1131 sm = image.getSampleModel(); 1132 } 1133 1134 if (sm instanceof ComponentSampleModel) { 1135 // Ensure image rows samples are stored contiguously 1136 // in a single bank. 1137 ComponentSampleModel csm = (ComponentSampleModel)sm; 1138 1139 if (csm.getPixelStride() != csm.getNumBands()) { 1140 return false; 1141 } 1142 1143 int[] bandOffsets = csm.getBandOffsets(); 1144 for (int i = 0; i < bandOffsets.length; i++) { 1145 if (bandOffsets[i] != i) { 1146 return false; 1147 } 1148 } 1149 1150 int[] bankIndices = csm.getBankIndices(); 1151 for (int i = 0; i < bandOffsets.length; i++) { 1152 if (bankIndices[i] != 0) { 1153 return false; 1154 } 1155 } 1156 1157 return true; 1158 } 1159 1160 // Otherwise true if and only if it's a bilevel image with 1161 // a MultiPixelPackedSampleModel, 1 bit per pixel, and 1 bit 1162 // pixel stride. 1163 return ImageUtil.isBinary(sm); 1164 } 1165 }