001/* Copyright (C) 2004, 2006, Free Software Foundation 002 003This file is part of GNU Classpath. 004 005GNU Classpath is free software; you can redistribute it and/or modify 006it under the terms of the GNU General Public License as published by 007the Free Software Foundation; either version 2, or (at your option) 008any later version. 009 010GNU Classpath is distributed in the hope that it will be useful, but 011WITHOUT ANY WARRANTY; without even the implied warranty of 012MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013General Public License for more details. 014 015You should have received a copy of the GNU General Public License 016along with GNU Classpath; see the file COPYING. If not, write to the 017Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01802110-1301 USA. 019 020Linking this library statically or dynamically with other modules is 021making a combined work based on this library. Thus, the terms and 022conditions of the GNU General Public License cover the whole 023combination. 024 025As a special exception, the copyright holders of this library give you 026permission to link this library with independent modules to produce an 027executable, regardless of the license terms of these independent 028modules, and to copy and distribute the resulting executable under 029terms of your choice, provided that you also meet, for each linked 030independent module, the terms and conditions of the license of that 031module. An independent module is a module which is not derived from 032or based on this library. If you modify this library, you may extend 033this exception to your version of the library, but you are not 034obligated to do so. If you do not wish to do so, delete this 035exception statement from your version. */ 036 037package java.awt.image; 038 039import gnu.java.awt.Buffers; 040import gnu.java.lang.CPStringBuilder; 041 042/** 043 * MultiPixelPackedSampleModel provides a single band model that supports 044 * multiple pixels in a single unit. Pixels have 2^n bits and 2^k pixels fit 045 * per data element. 046 * 047 * @author Jerry Quinn (jlquinn@optonline.net) 048 */ 049public class MultiPixelPackedSampleModel extends SampleModel 050{ 051 private int scanlineStride; 052 private int[] bitMasks; 053 private int[] bitOffsets; 054 private int[] sampleSize; 055 private int dataBitOffset; 056 private int elemBits; 057 private int numberOfBits; 058 private int numElems; 059 060 /** 061 * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified 062 * data type, which should be one of: 063 * <ul> 064 * <li>{@link DataBuffer#TYPE_BYTE};</li> 065 * <li>{@link DataBuffer#TYPE_USHORT};</li> 066 * <li>{@link DataBuffer#TYPE_INT};</li> 067 * </ul> 068 * 069 * @param dataType the data type. 070 * @param w the width (in pixels). 071 * @param h the height (in pixels). 072 * @param numberOfBits the number of bits per pixel (must be a power of 2). 073 */ 074 public MultiPixelPackedSampleModel(int dataType, int w, int h, 075 int numberOfBits) 076 { 077 this(dataType, w, h, numberOfBits, 0, 0); 078 } 079 080 /** 081 * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified 082 * data type, which should be one of: 083 * <ul> 084 * <li>{@link DataBuffer#TYPE_BYTE};</li> 085 * <li>{@link DataBuffer#TYPE_USHORT};</li> 086 * <li>{@link DataBuffer#TYPE_INT};</li> 087 * </ul> 088 * 089 * @param dataType the data type. 090 * @param w the width (in pixels). 091 * @param h the height (in pixels). 092 * @param numberOfBits the number of bits per pixel (must be a power of 2). 093 * @param scanlineStride the number of data elements from a pixel on one 094 * row to the corresponding pixel in the next row. 095 * @param dataBitOffset the offset to the first data bit. 096 */ 097 public MultiPixelPackedSampleModel(int dataType, int w, int h, 098 int numberOfBits, int scanlineStride, 099 int dataBitOffset) 100 { 101 super(dataType, w, h, 1); 102 103 switch (dataType) 104 { 105 case DataBuffer.TYPE_BYTE: 106 elemBits = 8; 107 break; 108 case DataBuffer.TYPE_USHORT: 109 elemBits = 16; 110 break; 111 case DataBuffer.TYPE_INT: 112 elemBits = 32; 113 break; 114 default: 115 throw new IllegalArgumentException("MultiPixelPackedSampleModel" 116 + " unsupported dataType"); 117 } 118 119 this.dataBitOffset = dataBitOffset; 120 121 this.numberOfBits = numberOfBits; 122 if (numberOfBits > elemBits) 123 throw new RasterFormatException("MultiPixelPackedSampleModel pixel size" 124 + " larger than dataType"); 125 switch (numberOfBits) 126 { 127 case 1: case 2: case 4: case 8: case 16: case 32: break; 128 default: 129 throw new RasterFormatException("MultiPixelPackedSampleModel pixel" 130 + " size not 2^n bits"); 131 } 132 numElems = elemBits / numberOfBits; 133 134 // Compute scan line large enough for w pixels. 135 if (scanlineStride == 0) 136 scanlineStride = ((dataBitOffset + w * numberOfBits) - 1) / elemBits + 1; 137 this.scanlineStride = scanlineStride; 138 139 140 sampleSize = new int[1]; 141 sampleSize[0] = numberOfBits; 142 143 bitMasks = new int[numElems]; 144 bitOffsets = new int[numElems]; 145 for (int i=0; i < numElems; i++) 146 { 147 bitOffsets[numElems - i- 1] = numberOfBits * i; 148 bitMasks[numElems - i - 1] = ((1 << numberOfBits) - 1) << 149 bitOffsets[numElems - i - 1]; 150 } 151 } 152 153 /** 154 * Creates a new <code>MultiPixelPackedSample</code> model with the same 155 * data type and bits per pixel as this model, but with the specified 156 * dimensions. 157 * 158 * @param w the width (in pixels). 159 * @param h the height (in pixels). 160 * 161 * @return The new sample model. 162 */ 163 public SampleModel createCompatibleSampleModel(int w, int h) 164 { 165 /* FIXME: We can avoid recalculation of bit offsets and sample 166 sizes here by passing these from the current instance to a 167 special private constructor. */ 168 return new MultiPixelPackedSampleModel(dataType, w, h, numberOfBits); 169 } 170 171 /** 172 * Creates a DataBuffer for holding pixel data in the format and 173 * layout described by this SampleModel. The returned buffer will 174 * consist of one single bank. 175 * 176 * @return A new data buffer. 177 */ 178 public DataBuffer createDataBuffer() 179 { 180 int size = scanlineStride * height; 181 if (dataBitOffset > 0) 182 size += (dataBitOffset - 1) / elemBits + 1; 183 return Buffers.createBuffer(getDataType(), size); 184 } 185 186 /** 187 * Returns the number of data elements required to transfer a pixel in the 188 * get/setDataElements() methods. 189 * 190 * @return <code>1</code>. 191 */ 192 public int getNumDataElements() 193 { 194 return 1; 195 } 196 197 /** 198 * Returns an array containing the size (in bits) of the samples in each 199 * band. The <code>MultiPixelPackedSampleModel</code> class supports only 200 * one band, so this method returns an array with length <code>1</code>. 201 * 202 * @return An array containing the size (in bits) of the samples in band zero. 203 * 204 * @see #getSampleSize(int) 205 */ 206 public int[] getSampleSize() 207 { 208 return (int[]) sampleSize.clone(); 209 } 210 211 /** 212 * Returns the size of the samples in the specified band. Note that the 213 * <code>MultiPixelPackedSampleModel</code> supports only one band -- this 214 * method ignored the <code>band</code> argument, and always returns the size 215 * of band zero. 216 * 217 * @param band the band (this parameter is ignored). 218 * 219 * @return The size of the samples in band zero. 220 * 221 * @see #getSampleSize() 222 */ 223 public int getSampleSize(int band) 224 { 225 return sampleSize[0]; 226 } 227 228 /** 229 * Returns the index in the data buffer that stores the pixel at (x, y). 230 * 231 * @param x the x-coordinate. 232 * @param y the y-coordinate. 233 * 234 * @return The index in the data buffer that stores the pixel at (x, y). 235 * 236 * @see #getBitOffset(int) 237 */ 238 public int getOffset(int x, int y) 239 { 240 return scanlineStride * y + ((dataBitOffset + x * numberOfBits) / elemBits); 241 } 242 243 /** 244 * The bit offset (within an element in the data buffer) of the pixels with 245 * the specified x-coordinate. 246 * 247 * @param x the x-coordinate. 248 * 249 * @return The bit offset. 250 */ 251 public int getBitOffset(int x) 252 { 253 return (dataBitOffset + x * numberOfBits) % elemBits; 254 } 255 256 /** 257 * Returns the offset to the first data bit. 258 * 259 * @return The offset to the first data bit. 260 */ 261 public int getDataBitOffset() 262 { 263 return dataBitOffset; 264 } 265 266 /** 267 * Returns the number of data elements from a pixel in one row to the 268 * corresponding pixel in the next row. 269 * 270 * @return The scanline stride. 271 */ 272 public int getScanlineStride() 273 { 274 return scanlineStride; 275 } 276 277 /** 278 * Returns the number of bits per pixel. 279 * 280 * @return The number of bits per pixel. 281 */ 282 public int getPixelBitStride() 283 { 284 return numberOfBits; 285 } 286 287 /** 288 * Returns the transfer type, which is one of the following (depending on 289 * the number of bits per sample for this model): 290 * <ul> 291 * <li>{@link DataBuffer#TYPE_BYTE};</li> 292 * <li>{@link DataBuffer#TYPE_USHORT};</li> 293 * <li>{@link DataBuffer#TYPE_INT};</li> 294 * </ul> 295 * 296 * @return The transfer type. 297 */ 298 public int getTransferType() 299 { 300 if (numberOfBits <= DataBuffer.getDataTypeSize(DataBuffer.TYPE_BYTE)) 301 return DataBuffer.TYPE_BYTE; 302 else if (numberOfBits <= DataBuffer.getDataTypeSize(DataBuffer.TYPE_USHORT)) 303 return DataBuffer.TYPE_USHORT; 304 return DataBuffer.TYPE_INT; 305 } 306 307 /** 308 * Normally this method returns a sample model for accessing a subset of 309 * bands of image data, but since <code>MultiPixelPackedSampleModel</code> 310 * only supports a single band, this overridden implementation just returns 311 * a new instance of <code>MultiPixelPackedSampleModel</code>, with the same 312 * attributes as this instance. 313 * 314 * @param bands the bands to include in the subset (this is ignored, except 315 * that if it is non-<code>null</code> a check is made to ensure that the 316 * array length is equal to <code>1</code>). 317 * 318 * @throws RasterFormatException if <code>bands</code> is not 319 * <code>null</code> and <code>bands.length != 1</code>. 320 */ 321 public SampleModel createSubsetSampleModel(int[] bands) 322 { 323 if (bands != null && bands.length != 1) 324 throw new RasterFormatException("MultiPixelPackedSampleModel only" 325 + " supports one band"); 326 return new MultiPixelPackedSampleModel(dataType, width, height, 327 numberOfBits, scanlineStride, dataBitOffset); 328 } 329 330 /** 331 * Extract one pixel and return in an array of transfer type. 332 * 333 * Extracts the pixel at x, y from data and stores into the 0th index of the 334 * array obj, since there is only one band. If obj is null, a new array of 335 * getTransferType() is created. 336 * 337 * @param x The x-coordinate of the pixel rectangle to store in 338 * <code>obj</code>. 339 * @param y The y-coordinate of the pixel rectangle to store in 340 * <code>obj</code>. 341 * @param obj The primitive array to store the pixels into or null to force 342 * creation. 343 * @param data The DataBuffer that is the source of the pixel data. 344 * @return The primitive array containing the pixel data. 345 * @see java.awt.image.SampleModel#getDataElements(int, int, Object, 346 * DataBuffer) 347 */ 348 public Object getDataElements(int x, int y, Object obj, DataBuffer data) 349 { 350 int pixel = getSample(x, y, 0, data); 351 switch (getTransferType()) 352 { 353 case DataBuffer.TYPE_BYTE: 354 if (obj == null) 355 obj = new byte[1]; 356 ((byte[]) obj)[0] = (byte) pixel; 357 return obj; 358 case DataBuffer.TYPE_USHORT: 359 if (obj == null) 360 obj = new short[1]; 361 ((short[]) obj)[0] = (short) pixel; 362 return obj; 363 case DataBuffer.TYPE_INT: 364 if (obj == null) 365 obj = new int[1]; 366 ((int[]) obj)[0] = pixel; 367 return obj; 368 default: 369 // Seems like the only sensible thing to do. 370 throw new ClassCastException(); 371 } 372 } 373 374 /** 375 * Returns an array (of length 1) containing the sample for the pixel at 376 * (x, y) in the specified data buffer. If <code>iArray</code> is not 377 * <code>null</code>, it will be populated with the sample value and 378 * returned as the result of this function (this avoids allocating a new 379 * array instance). 380 * 381 * @param x the x-coordinate of the pixel. 382 * @param y the y-coordinate of the pixel. 383 * @param iArray an array to populate with the sample values and return as 384 * the result (if <code>null</code>, a new array will be allocated). 385 * @param data the data buffer (<code>null</code> not permitted). 386 * 387 * @return An array containing the pixel sample value. 388 * 389 * @throws NullPointerException if <code>data</code> is <code>null</code>. 390 */ 391 public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) 392 { 393 if (iArray == null) 394 iArray = new int[1]; 395 iArray[0] = getSample(x, y, 0, data); 396 return iArray; 397 } 398 399 /** 400 * Returns the sample value for the pixel at (x, y) in the specified data 401 * buffer. 402 * 403 * @param x the x-coordinate of the pixel. 404 * @param y the y-coordinate of the pixel. 405 * @param b the band (in the range <code>0</code> to 406 * <code>getNumBands() - 1</code>). 407 * @param data the data buffer (<code>null</code> not permitted). 408 * 409 * @return The sample value. 410 * 411 * @throws NullPointerException if <code>data</code> is <code>null</code>. 412 */ 413 public int getSample(int x, int y, int b, DataBuffer data) 414 { 415 int pos = 416 ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits; 417 int offset = getOffset(x, y); 418 int samples = data.getElem(offset); 419 return (samples & bitMasks[pos]) >>> bitOffsets[pos]; 420 } 421 422 /** 423 * Set the pixel at x, y to the value in the first element of the primitive 424 * array obj. 425 * 426 * @param x The x-coordinate of the data elements in <code>obj</code>. 427 * @param y The y-coordinate of the data elements in <code>obj</code>. 428 * @param obj The primitive array containing the data elements to set. 429 * @param data The DataBuffer to store the data elements into. 430 */ 431 public void setDataElements(int x, int y, Object obj, DataBuffer data) 432 { 433 int transferType = getTransferType(); 434 try 435 { 436 switch (transferType) 437 { 438 case DataBuffer.TYPE_BYTE: 439 { 440 byte[] in = (byte[]) obj; 441 setSample(x, y, 0, in[0] & 0xFF, data); 442 return; 443 } 444 case DataBuffer.TYPE_USHORT: 445 { 446 short[] in = (short[]) obj; 447 setSample(x, y, 0, in[0] & 0xFFFF, data); 448 return; 449 } 450 case DataBuffer.TYPE_INT: 451 { 452 int[] in = (int[]) obj; 453 setSample(x, y, 0, in[0], data); 454 return; 455 } 456 default: 457 throw new ClassCastException("Unsupported data type"); 458 } 459 } 460 catch (ArrayIndexOutOfBoundsException aioobe) 461 { 462 String msg = "While writing data elements" + 463 ", x=" + x + ", y=" + y + 464 ", width=" + width + ", height=" + height + 465 ", scanlineStride=" + scanlineStride + 466 ", offset=" + getOffset(x, y) + 467 ", data.getSize()=" + data.getSize() + 468 ", data.getOffset()=" + data.getOffset() + 469 ": " + aioobe; 470 throw new ArrayIndexOutOfBoundsException(msg); 471 } 472 } 473 474 /** 475 * Sets the sample value for the pixel at (x, y) in the specified data 476 * buffer to the specified value. 477 * 478 * @param x the x-coordinate of the pixel. 479 * @param y the y-coordinate of the pixel. 480 * @param iArray the sample value (<code>null</code> not permitted). 481 * @param data the data buffer (<code>null</code> not permitted). 482 * 483 * @throws NullPointerException if either <code>iArray</code> or 484 * <code>data</code> is <code>null</code>. 485 * 486 * @see #setSample(int, int, int, int, DataBuffer) 487 */ 488 public void setPixel(int x, int y, int[] iArray, DataBuffer data) 489 { 490 setSample(x, y, 0, iArray[0], data); 491 } 492 493 /** 494 * Sets the sample value for a band for the pixel at (x, y) in the 495 * specified data buffer. 496 * 497 * @param x the x-coordinate of the pixel. 498 * @param y the y-coordinate of the pixel. 499 * @param b the band (in the range <code>0</code> to 500 * <code>getNumBands() - 1</code>). 501 * @param s the sample value. 502 * @param data the data buffer (<code>null</code> not permitted). 503 * 504 * @throws NullPointerException if <code>data</code> is <code>null</code>. 505 */ 506 public void setSample(int x, int y, int b, int s, DataBuffer data) 507 { 508 int bitpos = 509 ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits; 510 int offset = getOffset(x, y); 511 512 s = s << bitOffsets[bitpos]; 513 s = s & bitMasks[bitpos]; 514 515 int sample = data.getElem(offset); 516 sample |= s; 517 data.setElem(offset, sample); 518 } 519 520 /** 521 * Tests this sample model for equality with an arbitrary object. This 522 * method returns <code>true</code> if and only if: 523 * <ul> 524 * <li><code>obj</code> is not <code>null</code>; 525 * <li><code>obj</code> is an instance of 526 * <code>MultiPixelPackedSampleModel</code>; 527 * <li>both models have the same: 528 * <ul> 529 * <li><code>dataType</code>; 530 * <li><code>width</code>; 531 * <li><code>height</code>; 532 * <li><code>numberOfBits</code>; 533 * <li><code>scanlineStride</code>; 534 * <li><code>dataBitOffsets</code>. 535 * </ul> 536 * </li> 537 * </ul> 538 * 539 * @param obj the object (<code>null</code> permitted) 540 * 541 * @return <code>true</code> if this model is equal to <code>obj</code>, and 542 * <code>false</code> otherwise. 543 */ 544 public boolean equals(Object obj) 545 { 546 if (this == obj) 547 return true; 548 if (! (obj instanceof MultiPixelPackedSampleModel)) 549 return false; 550 MultiPixelPackedSampleModel that = (MultiPixelPackedSampleModel) obj; 551 if (this.dataType != that.dataType) 552 return false; 553 if (this.width != that.width) 554 return false; 555 if (this.height != that.height) 556 return false; 557 if (this.numberOfBits != that.numberOfBits) 558 return false; 559 if (this.scanlineStride != that.scanlineStride) 560 return false; 561 if (this.dataBitOffset != that.dataBitOffset) 562 return false; 563 return true; 564 } 565 566 /** 567 * Returns a hash code for this <code>MultiPixelPackedSampleModel</code>. 568 * 569 * @return A hash code. 570 */ 571 public int hashCode() 572 { 573 // this hash code won't match Sun's, but that shouldn't matter... 574 int result = 193; 575 result = 37 * result + dataType; 576 result = 37 * result + width; 577 result = 37 * result + height; 578 result = 37 * result + numberOfBits; 579 result = 37 * result + scanlineStride; 580 result = 37 * result + dataBitOffset; 581 return result; 582 } 583 584 /** 585 * Creates a String with some information about this SampleModel. 586 * @return A String describing this SampleModel. 587 * @see java.lang.Object#toString() 588 */ 589 public String toString() 590 { 591 CPStringBuilder result = new CPStringBuilder(); 592 result.append(getClass().getName()); 593 result.append("["); 594 result.append("scanlineStride=").append(scanlineStride); 595 for(int i=0; i < bitMasks.length; i+=1) 596 { 597 result.append(", mask[").append(i).append("]=0x").append(Integer.toHexString(bitMasks[i])); 598 } 599 600 result.append("]"); 601 return result.toString(); 602 } 603}