1 /* 2 * Copyright (c) 2003, 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 (int)(((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] |= 605 (int)((binaryDataArray[b++] & 0xFF) << shift); 606 shift -= 8; 607 xRemaining -= 8; 608 } 609 eltOffset += lineStride; 610 } 611 } 612 } else { // bitOffset != 0 613 int stride = (rectWidth + 7)/8; 614 int offset = 0; 615 if(dataBuffer instanceof DataBufferByte) { 616 byte[] data = ((DataBufferByte)dataBuffer).getData(); 617 618 if((bitOffset & 7) == 0) { 619 for(int y = 0; y < rectHeight; y++) { 620 System.arraycopy(binaryDataArray, offset, 621 data, eltOffset, 622 stride); 623 offset += stride; 624 eltOffset += lineStride; 625 } 626 } else { // bitOffset % 8 != 0 627 int rightShift = bitOffset & 7; 628 int leftShift = 8 - rightShift; 629 int leftShift8 = 8 + leftShift; 630 int mask = (byte)(255<<leftShift); 631 int mask1 = (byte)~mask; 632 633 for(int y = 0; y < rectHeight; y++) { 634 int i = eltOffset; 635 int xRemaining = rectWidth; 636 while(xRemaining > 0) { 637 byte datum = binaryDataArray[b++]; 638 639 if (xRemaining > leftShift8) { 640 // when all the bits in this BYTE will be set 641 // into the data buffer. 642 data[i] = (byte)((data[i] & mask ) | 643 ((datum&0xFF) >>> rightShift)); 644 data[++i] = (byte)((datum & 0xFF) << leftShift); 645 } else if (xRemaining > leftShift) { 646 // All the "leftShift" high bits will be set 647 // into the data buffer. But not all the 648 // "rightShift" low bits will be set. 649 data[i] = (byte)((data[i] & mask ) | 650 ((datum&0xFF) >>> rightShift)); 651 i++; 652 data[i] = 653 (byte)((data[i] & mask1) | ((datum & 0xFF) << leftShift)); 654 } 655 else { 656 // Less than "leftShift" high bits will be set. 657 int remainMask = (1 << leftShift - xRemaining) - 1; 658 data[i] = 659 (byte)((data[i] & (mask | remainMask)) | 660 (datum&0xFF) >>> rightShift & ~remainMask); 661 } 662 xRemaining -= 8; 663 } 664 eltOffset += lineStride; 665 } 666 } 667 } else if(dataBuffer instanceof DataBufferShort || 668 dataBuffer instanceof DataBufferUShort) { 669 short[] data = dataBuffer instanceof DataBufferShort ? 670 ((DataBufferShort)dataBuffer).getData() : 671 ((DataBufferUShort)dataBuffer).getData(); 672 673 int rightShift = bitOffset & 7; 674 int leftShift = 8 - rightShift; 675 int leftShift16 = 16 + leftShift; 676 int mask = (short)(~(255 << leftShift)); 677 int mask1 = (short)(65535 << leftShift); 678 int mask2 = (short)~mask1; 679 680 for(int y = 0; y < rectHeight; y++) { 681 int bOffset = bitOffset; 682 int xRemaining = rectWidth; 683 for(int x = 0; x < rectWidth; 684 x += 8, bOffset += 8, xRemaining -= 8) { 685 int i = eltOffset + (bOffset >> 4); 686 int mod = bOffset & 15; 687 int datum = binaryDataArray[b++] & 0xFF; 688 if(mod <= 8) { 689 // This BYTE is set into one SHORT 690 if (xRemaining < 8) { 691 // Mask the bits to be set. 692 datum &= 255 << 8 - xRemaining; 693 } 694 data[i] = (short)((data[i] & mask) | (datum << leftShift)); 695 } else if (xRemaining > leftShift16) { 696 // This BYTE will be set into two SHORTs 697 data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF)); 698 data[++i] = 699 (short)((datum << leftShift)&0xFFFF); 700 } else if (xRemaining > leftShift) { 701 // This BYTE will be set into two SHORTs; 702 // But not all the low bits will be set into SHORT 703 data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF)); 704 i++; 705 data[i] = 706 (short)((data[i] & mask2) | ((datum << leftShift)&0xFFFF)); 707 } else { 708 // Only some of the high bits will be set into 709 // SHORTs 710 int remainMask = (1 << leftShift - xRemaining) - 1; 711 data[i] = (short)((data[i] & (mask1 | remainMask)) | 712 ((datum >>> rightShift)&0xFFFF & ~remainMask)); 713 } 714 } 715 eltOffset += lineStride; 716 } 717 } else if(dataBuffer instanceof DataBufferInt) { 718 int[] data = ((DataBufferInt)dataBuffer).getData(); 719 int rightShift = bitOffset & 7; 720 int leftShift = 8 - rightShift; 721 int leftShift32 = 32 + leftShift; 722 int mask = 0xFFFFFFFF << leftShift; 723 int mask1 = ~mask; 724 725 for(int y = 0; y < rectHeight; y++) { 726 int bOffset = bitOffset; 727 int xRemaining = rectWidth; 728 for(int x = 0; x < rectWidth; 729 x += 8, bOffset += 8, xRemaining -= 8) { 730 int i = eltOffset + (bOffset >> 5); 731 int mod = bOffset & 31; 732 int datum = binaryDataArray[b++] & 0xFF; 733 if(mod <= 24) { 734 // This BYTE is set into one INT 735 int shift = 24 - mod; 736 if (xRemaining < 8) { 737 // Mask the bits to be set. 738 datum &= 255 << 8 - xRemaining; 739 } 740 data[i] = (data[i] & (~(255 << shift))) | (datum << shift); 741 } else if (xRemaining > leftShift32) { 742 // All the bits of this BYTE will be set into two INTs 743 data[i] = (data[i] & mask) | (datum >>> rightShift); 744 data[++i] = datum << leftShift; 745 } else if (xRemaining > leftShift) { 746 // This BYTE will be set into two INTs; 747 // But not all the low bits will be set into INT 748 data[i] = (data[i] & mask) | (datum >>> rightShift); 749 i++; 750 data[i] = (data[i] & mask1) | (datum << leftShift); 751 } else { 752 // Only some of the high bits will be set into INT 753 int remainMask = (1 << leftShift - xRemaining) - 1; 754 data[i] = (data[i] & (mask | remainMask)) | 755 (datum >>> rightShift & ~remainMask); 756 } 757 } 758 eltOffset += lineStride; 759 } 760 } 761 } 762 } 763 764 /** 765 * Copies data into the packed array of the <code>Raster</code> 766 * from an array of unpacked data of the form returned by 767 * <code>getUnpackedBinaryData()</code>. 768 * 769 * <p> If the data are binary, then the target bit will be set if 770 * and only if the corresponding byte is non-zero. 771 * 772 * @throws IllegalArgumentException if <code>isBinary()</code> returns 773 * <code>false</code> with the <code>SampleModel</code> of the 774 * supplied <code>Raster</code> as argument. 775 */ 776 public static void setUnpackedBinaryData(byte[] bdata, 777 WritableRaster raster, 778 Rectangle rect) { 779 SampleModel sm = raster.getSampleModel(); 780 if(!isBinary(sm)) { 781 throw new IllegalArgumentException(I18N.getString("ImageUtil0")); 782 } 783 784 int rectX = rect.x; 785 int rectY = rect.y; 786 int rectWidth = rect.width; 787 int rectHeight = rect.height; 788 789 DataBuffer dataBuffer = raster.getDataBuffer(); 790 791 int dx = rectX - raster.getSampleModelTranslateX(); 792 int dy = rectY - raster.getSampleModelTranslateY(); 793 794 MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; 795 int lineStride = mpp.getScanlineStride(); 796 int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); 797 int bitOffset = mpp.getBitOffset(dx); 798 799 int k = 0; 800 801 if(dataBuffer instanceof DataBufferByte) { 802 byte[] data = ((DataBufferByte)dataBuffer).getData(); 803 for(int y = 0; y < rectHeight; y++) { 804 int bOffset = eltOffset*8 + bitOffset; 805 for(int x = 0; x < rectWidth; x++) { 806 if(bdata[k++] != (byte)0) { 807 data[bOffset/8] |= 808 (byte)(0x00000001 << (7 - bOffset & 7)); 809 } 810 bOffset++; 811 } 812 eltOffset += lineStride; 813 } 814 } else if(dataBuffer instanceof DataBufferShort || 815 dataBuffer instanceof DataBufferUShort) { 816 short[] data = dataBuffer instanceof DataBufferShort ? 817 ((DataBufferShort)dataBuffer).getData() : 818 ((DataBufferUShort)dataBuffer).getData(); 819 for(int y = 0; y < rectHeight; y++) { 820 int bOffset = eltOffset*16 + bitOffset; 821 for(int x = 0; x < rectWidth; x++) { 822 if(bdata[k++] != (byte)0) { 823 data[bOffset/16] |= 824 (short)(0x00000001 << 825 (15 - bOffset % 16)); 826 } 827 bOffset++; 828 } 829 eltOffset += lineStride; 830 } 831 } else if(dataBuffer instanceof DataBufferInt) { 832 int[] data = ((DataBufferInt)dataBuffer).getData(); 833 for(int y = 0; y < rectHeight; y++) { 834 int bOffset = eltOffset*32 + bitOffset; 835 for(int x = 0; x < rectWidth; x++) { 836 if(bdata[k++] != (byte)0) { 837 data[bOffset/32] |= 838 (int)(0x00000001 << 839 (31 - bOffset % 32)); 840 } 841 bOffset++; 842 } 843 eltOffset += lineStride; 844 } 845 } 846 } 847 848 public static boolean isBinary(SampleModel sm) { 849 return sm instanceof MultiPixelPackedSampleModel && 850 ((MultiPixelPackedSampleModel)sm).getPixelBitStride() == 1 && 851 sm.getNumBands() == 1; 852 } 853 854 public static ColorModel createColorModel(ColorSpace colorSpace, 855 SampleModel sampleModel) { 856 ColorModel colorModel = null; 857 858 if(sampleModel == null) { 859 throw new IllegalArgumentException(I18N.getString("ImageUtil1")); 860 } 861 862 int numBands = sampleModel.getNumBands(); 863 if (numBands < 1 || numBands > 4) { 864 return null; 865 } 866 867 int dataType = sampleModel.getDataType(); 868 if (sampleModel instanceof ComponentSampleModel) { 869 if (dataType < DataBuffer.TYPE_BYTE || 870 //dataType == DataBuffer.TYPE_SHORT || 871 dataType > DataBuffer.TYPE_DOUBLE) { 872 return null; 873 } 874 875 if (colorSpace == null) 876 colorSpace = 877 numBands <= 2 ? 878 ColorSpace.getInstance(ColorSpace.CS_GRAY) : 879 ColorSpace.getInstance(ColorSpace.CS_sRGB); 880 881 boolean useAlpha = (numBands == 2) || (numBands == 4); 882 int transparency = useAlpha ? 883 Transparency.TRANSLUCENT : Transparency.OPAQUE; 884 885 boolean premultiplied = false; 886 887 int dataTypeSize = DataBuffer.getDataTypeSize(dataType); 888 int[] bits = new int[numBands]; 889 for (int i = 0; i < numBands; i++) { 890 bits[i] = dataTypeSize; 891 } 892 893 colorModel = new ComponentColorModel(colorSpace, 894 bits, 895 useAlpha, 896 premultiplied, 897 transparency, 898 dataType); 899 } else if (sampleModel instanceof SinglePixelPackedSampleModel) { 900 SinglePixelPackedSampleModel sppsm = 901 (SinglePixelPackedSampleModel)sampleModel; 902 903 int[] bitMasks = sppsm.getBitMasks(); 904 int rmask = 0; 905 int gmask = 0; 906 int bmask = 0; 907 int amask = 0; 908 909 numBands = bitMasks.length; 910 if (numBands <= 2) { 911 rmask = gmask = bmask = bitMasks[0]; 912 if (numBands == 2) { 913 amask = bitMasks[1]; 914 } 915 } else { 916 rmask = bitMasks[0]; 917 gmask = bitMasks[1]; 918 bmask = bitMasks[2]; 919 if (numBands == 4) { 920 amask = bitMasks[3]; 921 } 922 } 923 924 int[] sampleSize = sppsm.getSampleSize(); 925 int bits = 0; 926 for (int i = 0; i < sampleSize.length; i++) { 927 bits += sampleSize[i]; 928 } 929 930 if (colorSpace == null) 931 colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); 932 933 colorModel = 934 new DirectColorModel(colorSpace, 935 bits, rmask, gmask, bmask, amask, 936 false, 937 sampleModel.getDataType()); 938 } else if (sampleModel instanceof MultiPixelPackedSampleModel) { 939 int bits = 940 ((MultiPixelPackedSampleModel)sampleModel).getPixelBitStride(); 941 int size = 1 << bits; 942 byte[] comp = new byte[size]; 943 944 for (int i = 0; i < size; i++) 945 comp[i] = (byte)(255 * i / (size - 1)); 946 947 colorModel = new IndexColorModel(bits, size, comp, comp, comp); 948 } 949 950 return colorModel; 951 } 952 953 public static int getElementSize(SampleModel sm) { 954 int elementSize = DataBuffer.getDataTypeSize(sm.getDataType()); 955 956 if (sm instanceof MultiPixelPackedSampleModel) { 957 MultiPixelPackedSampleModel mppsm = 958 (MultiPixelPackedSampleModel)sm; 959 return mppsm.getSampleSize(0) * mppsm.getNumBands(); 960 } else if (sm instanceof ComponentSampleModel) { 961 return sm.getNumBands() * elementSize; 962 } else if (sm instanceof SinglePixelPackedSampleModel) { 963 return elementSize; 964 } 965 966 return elementSize * sm.getNumBands(); 967 968 } 969 970 public static long getTileSize(SampleModel sm) { 971 int elementSize = DataBuffer.getDataTypeSize(sm.getDataType()); 972 973 if (sm instanceof MultiPixelPackedSampleModel) { 974 MultiPixelPackedSampleModel mppsm = 975 (MultiPixelPackedSampleModel)sm; 976 return (mppsm.getScanlineStride() * mppsm.getHeight() + 977 (mppsm.getDataBitOffset() + elementSize -1) / elementSize) * 978 ((elementSize + 7) / 8); 979 } else if (sm instanceof ComponentSampleModel) { 980 ComponentSampleModel csm = (ComponentSampleModel)sm; 981 int[] bandOffsets = csm.getBandOffsets(); 982 int maxBandOff = bandOffsets[0]; 983 for (int i=1; i<bandOffsets.length; i++) 984 maxBandOff = Math.max(maxBandOff, bandOffsets[i]); 985 986 long size = 0; 987 int pixelStride = csm.getPixelStride(); 988 int scanlineStride = csm.getScanlineStride(); 989 if (maxBandOff >= 0) 990 size += maxBandOff + 1; 991 if (pixelStride > 0) 992 size += pixelStride * (sm.getWidth() - 1); 993 if (scanlineStride > 0) 994 size += scanlineStride * (sm.getHeight() - 1); 995 996 int[] bankIndices = csm.getBankIndices(); 997 maxBandOff = bankIndices[0]; 998 for (int i=1; i<bankIndices.length; i++) 999 maxBandOff = Math.max(maxBandOff, bankIndices[i]); 1000 return size * (maxBandOff + 1) * ((elementSize + 7) / 8); 1001 } else if (sm instanceof SinglePixelPackedSampleModel) { 1002 SinglePixelPackedSampleModel sppsm = 1003 (SinglePixelPackedSampleModel)sm; 1004 long size = sppsm.getScanlineStride() * (sppsm.getHeight() - 1) + 1005 sppsm.getWidth(); 1006 return size * ((elementSize + 7) / 8); 1007 } 1008 1009 return 0; 1010 } 1011 1012 public static long getBandSize(SampleModel sm) { 1013 int elementSize = DataBuffer.getDataTypeSize(sm.getDataType()); 1014 1015 if (sm instanceof ComponentSampleModel) { 1016 ComponentSampleModel csm = (ComponentSampleModel)sm; 1017 int pixelStride = csm.getPixelStride(); 1018 int scanlineStride = csm.getScanlineStride(); 1019 long size = Math.min(pixelStride, scanlineStride); 1020 1021 if (pixelStride > 0) 1022 size += pixelStride * (sm.getWidth() - 1); 1023 if (scanlineStride > 0) 1024 size += scanlineStride * (sm.getHeight() - 1); 1025 return size * ((elementSize + 7) / 8); 1026 } else 1027 return getTileSize(sm); 1028 } 1029 /** 1030 * Tests whether the color indices represent a gray-scale image. 1031 * 1032 * @param r The red channel color indices. 1033 * @param g The green channel color indices. 1034 * @param b The blue channel color indices. 1035 * @return If all the indices have 256 entries, and are identical mappings, 1036 * return <code>true</code>; otherwise, return <code>false</code>. 1037 */ 1038 public static boolean isIndicesForGrayscale(byte[] r, byte[] g, byte[] b) { 1039 if (r.length != g.length || r.length != b.length) 1040 return false; 1041 1042 int size = r.length; 1043 1044 if (size != 256) 1045 return false; 1046 1047 for (int i = 0; i < size; i++) { 1048 byte temp = (byte) i; 1049 1050 if (r[i] != temp || g[i] != temp || b[i] != temp) 1051 return false; 1052 } 1053 1054 return true; 1055 } 1056 1057 /** Converts the provided object to <code>String</code> */ 1058 public static String convertObjectToString(Object obj) { 1059 if (obj == null) 1060 return ""; 1061 1062 String s = ""; 1063 if (obj instanceof byte[]) { 1064 byte[] bArray = (byte[])obj; 1065 for (int i = 0; i < bArray.length; i++) 1066 s += bArray[i] + " "; 1067 return s; 1068 } 1069 1070 if (obj instanceof int[]) { 1071 int[] iArray = (int[])obj; 1072 for (int i = 0; i < iArray.length; i++) 1073 s += iArray[i] + " " ; 1074 return s; 1075 } 1076 1077 if (obj instanceof short[]) { 1078 short[] sArray = (short[])obj; 1079 for (int i = 0; i < sArray.length; i++) 1080 s += sArray[i] + " " ; 1081 return s; 1082 } 1083 1084 return obj.toString(); 1085 1086 } 1087 1088 /** Checks that the provided <code>ImageWriter</code> can encode 1089 * the provided <code>ImageTypeSpecifier</code> or not. If not, an 1090 * <code>IIOException</code> will be thrown. 1091 * @param writer The provided <code>ImageWriter</code>. 1092 * @param type The image to be tested. 1093 * @throws IIOException If the writer cannot encoded the provided image. 1094 */ 1095 public static final void canEncodeImage(ImageWriter writer, 1096 ImageTypeSpecifier type) 1097 throws IIOException { 1098 ImageWriterSpi spi = writer.getOriginatingProvider(); 1099 1100 if(type != null && spi != null && !spi.canEncodeImage(type)) { 1101 throw new IIOException(I18N.getString("ImageUtil2")+" "+ 1102 writer.getClass().getName()); 1103 } 1104 } 1105 1106 /** Checks that the provided <code>ImageWriter</code> can encode 1107 * the provided <code>ColorModel</code> and <code>SampleModel</code>. 1108 * If not, an <code>IIOException</code> will be thrown. 1109 * @param writer The provided <code>ImageWriter</code>. 1110 * @param colorModel The provided <code>ColorModel</code>. 1111 * @param sampleModel The provided <code>SampleModel</code>. 1112 * @throws IIOException If the writer cannot encoded the provided image. 1113 */ 1114 public static final void canEncodeImage(ImageWriter writer, 1115 ColorModel colorModel, 1116 SampleModel sampleModel) 1117 throws IIOException { 1118 ImageTypeSpecifier type = null; 1119 if (colorModel != null && sampleModel != null) 1120 type = new ImageTypeSpecifier(colorModel, sampleModel); 1121 canEncodeImage(writer, type); 1122 } 1123 1124 /** 1125 * Returns whether the image has contiguous data across rows. 1126 */ 1127 public static final boolean imageIsContiguous(RenderedImage image) { 1128 SampleModel sm; 1129 if(image instanceof BufferedImage) { 1130 WritableRaster ras = ((BufferedImage)image).getRaster(); 1131 sm = ras.getSampleModel(); 1132 } else { 1133 sm = image.getSampleModel(); 1134 } 1135 1136 if (sm instanceof ComponentSampleModel) { 1137 // Ensure image rows samples are stored contiguously 1138 // in a single bank. 1139 ComponentSampleModel csm = (ComponentSampleModel)sm; 1140 1141 if (csm.getPixelStride() != csm.getNumBands()) { 1142 return false; 1143 } 1144 1145 int[] bandOffsets = csm.getBandOffsets(); 1146 for (int i = 0; i < bandOffsets.length; i++) { 1147 if (bandOffsets[i] != i) { 1148 return false; 1149 } 1150 } 1151 1152 int[] bankIndices = csm.getBankIndices(); 1153 for (int i = 0; i < bandOffsets.length; i++) { 1154 if (bankIndices[i] != 0) { 1155 return false; 1156 } 1157 } 1158 1159 return true; 1160 } 1161 1162 // Otherwise true if and only if it's a bilevel image with 1163 // a MultiPixelPackedSampleModel, 1 bit per pixel, and 1 bit 1164 // pixel stride. 1165 return ImageUtil.isBinary(sm); 1166 } 1167 }