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