001 /* Copyright (C) 2000, 2002, 2003, 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 java.util.Arrays; 040 041 import gnu.java.awt.BitMaskExtent; 042 043 /** 044 * A <code>SampleModel</code> used when all samples are stored in a single 045 * data element in the {@link DataBuffer}, and each data element contains 046 * samples for one pixel only. 047 * 048 * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) 049 */ 050 public class SinglePixelPackedSampleModel extends SampleModel 051 { 052 private int scanlineStride; 053 private int[] bitMasks; 054 private int[] bitOffsets; 055 private int[] sampleSize; 056 057 /** 058 * Creates a new <code>SinglePixelPackedSampleModel</code>. 059 * 060 * @param dataType the data buffer type. 061 * @param w the width (in pixels). 062 * @param h the height (in pixels). 063 * @param bitMasks an array containing the bit mask used to extract the 064 * sample value for each band. 065 */ 066 public SinglePixelPackedSampleModel(int dataType, int w, int h, 067 int[] bitMasks) 068 { 069 this(dataType, w, h, w, bitMasks); 070 } 071 072 /** 073 * Creates a new <code>SinglePixelPackedSampleModel</code>. 074 * 075 * @param dataType the data buffer type. 076 * @param w the width (in pixels). 077 * @param h the height (in pixels). 078 * @param scanlineStride the number of data elements between a pixel on one 079 * row and the corresponding pixel on the next row. 080 * @param bitMasks an array containing the bit mask used to extract the 081 * sample value for each band. 082 */ 083 public SinglePixelPackedSampleModel(int dataType, int w, int h, 084 int scanlineStride, int[] bitMasks) 085 { 086 super(dataType, w, h, bitMasks.length); 087 088 switch (dataType) 089 { 090 case DataBuffer.TYPE_BYTE: 091 case DataBuffer.TYPE_USHORT: 092 case DataBuffer.TYPE_INT: 093 break; 094 default: 095 throw new IllegalArgumentException( 096 "SinglePixelPackedSampleModel unsupported dataType"); 097 } 098 099 this.scanlineStride = scanlineStride; 100 this.bitMasks = bitMasks; 101 102 bitOffsets = new int[numBands]; 103 sampleSize = new int[numBands]; 104 105 BitMaskExtent extent = new BitMaskExtent(); 106 for (int b = 0; b < numBands; b++) 107 { 108 // the mask is an unsigned integer 109 long mask = bitMasks[b] & 0xFFFFFFFFL; 110 extent.setMask(mask); 111 sampleSize[b] = extent.bitWidth; 112 bitOffsets[b] = extent.leastSignificantBit; 113 } 114 } 115 116 /** 117 * Returns the number of data elements. 118 * 119 * @return <code>1</code>. 120 */ 121 public int getNumDataElements() 122 { 123 return 1; 124 } 125 126 /** 127 * Creates a new <code>SampleModel</code> that is compatible with this 128 * model and has the specified width and height. 129 * 130 * @param w the width (in pixels). 131 * @param h the height (in pixels). 132 * 133 * @return The new sample model. 134 */ 135 public SampleModel createCompatibleSampleModel(int w, int h) 136 { 137 /* FIXME: We can avoid recalculation of bit offsets and sample 138 sizes here by passing these from the current instance to a 139 special private constructor. */ 140 return new SinglePixelPackedSampleModel(dataType, w, h, bitMasks); 141 } 142 143 144 /** 145 * Creates a DataBuffer for holding pixel data in the format and 146 * layout described by this SampleModel. The returned buffer will 147 * consist of one single bank. 148 * 149 * @return The data buffer. 150 */ 151 public DataBuffer createDataBuffer() 152 { 153 // We can save (scanlineStride - width) pixels at the very end of 154 // the buffer. The Sun reference implementation (J2SE 1.3.1 and 155 // 1.4.1_01) seems to do this; tested with Mauve test code. 156 int size = scanlineStride * (height - 1) + width; 157 158 DataBuffer buffer = null; 159 switch (getTransferType()) 160 { 161 case DataBuffer.TYPE_BYTE: 162 buffer = new DataBufferByte(size); 163 break; 164 case DataBuffer.TYPE_USHORT: 165 buffer = new DataBufferUShort(size); 166 break; 167 case DataBuffer.TYPE_INT: 168 buffer = new DataBufferInt(size); 169 break; 170 } 171 return buffer; 172 } 173 174 /** 175 * Returns an array containing the size (in bits) for each band accessed by 176 * the <code>SampleModel</code>. 177 * 178 * @return An array. 179 * 180 * @see #getSampleSize(int) 181 */ 182 public int[] getSampleSize() 183 { 184 return (int[]) sampleSize.clone(); 185 } 186 187 /** 188 * Returns the size (in bits) of the samples for the specified band. 189 * 190 * @param band the band (in the range <code>0</code> to 191 * <code>getNumBands() - 1</code>). 192 * 193 * @return The sample size (in bits). 194 */ 195 public int getSampleSize(int band) 196 { 197 return sampleSize[band]; 198 } 199 200 /** 201 * Returns the index in the data buffer that stores the pixel at (x, y). 202 * 203 * @param x the x-coordinate. 204 * @param y the y-coordinate. 205 * 206 * @return The index in the data buffer that stores the pixel at (x, y). 207 */ 208 public int getOffset(int x, int y) 209 { 210 return scanlineStride*y + x; 211 } 212 213 public int[] getBitOffsets() 214 { 215 return bitOffsets; 216 } 217 218 public int[] getBitMasks() 219 { 220 return bitMasks; 221 } 222 223 /** 224 * Returns the number of data elements from a pixel in one row to the 225 * corresponding pixel in the next row. 226 * 227 * @return The scanline stride. 228 */ 229 public int getScanlineStride() 230 { 231 return scanlineStride; 232 } 233 234 /** 235 * Creates a new <code>SinglePixelPackedSampleModel</code> that accesses 236 * the specified subset of bands. 237 * 238 * @param bands an array containing band indices (<code>null</code> not 239 * permitted). 240 * 241 * @return A new sample model. 242 * 243 * @throws NullPointerException if <code>bands</code> is <code>null</code>. 244 * @throws RasterFormatException if <code>bands.length</code> is greater 245 * than the number of bands in this model. 246 */ 247 public SampleModel createSubsetSampleModel(int[] bands) 248 { 249 if (bands.length > numBands) 250 throw new RasterFormatException("Too many bands."); 251 252 int numBands = bands.length; 253 254 int[] bitMasks = new int[numBands]; 255 256 for (int b = 0; b < numBands; b++) 257 bitMasks[b] = this.bitMasks[bands[b]]; 258 259 return new SinglePixelPackedSampleModel(dataType, width, height, 260 scanlineStride, bitMasks); 261 } 262 263 public Object getDataElements(int x, int y, Object obj, 264 DataBuffer data) 265 { 266 int type = getTransferType(); 267 Object ret = null; 268 switch (type) 269 { 270 case DataBuffer.TYPE_BYTE: 271 { 272 byte[] in = (byte[]) obj; 273 if (in == null) 274 in = new byte[1]; 275 in[0] = (byte) data.getElem(x + y * scanlineStride); 276 ret = in; 277 } 278 break; 279 case DataBuffer.TYPE_USHORT: 280 { 281 short[] in = (short[]) obj; 282 if (in == null) 283 in = new short[1]; 284 in[0] = (short) data.getElem(x + y * scanlineStride); 285 ret = in; 286 } 287 break; 288 case DataBuffer.TYPE_INT: 289 { 290 int[] in = (int[]) obj; 291 if (in == null) 292 in = new int[1]; 293 in[0] = data.getElem(x + y * scanlineStride); 294 ret = in; 295 } 296 break; 297 } 298 return ret; 299 } 300 301 /** 302 * Returns an array containing the samples for the pixel at (x, y) in the 303 * specified data buffer. If <code>iArray</code> is not <code>null</code>, 304 * it will be populated with the sample values and returned as the result of 305 * this function (this avoids allocating a new array instance). 306 * 307 * @param x the x-coordinate of the pixel. 308 * @param y the y-coordinate of the pixel. 309 * @param iArray an array to populate with the sample values and return as 310 * the result (if <code>null</code>, a new array will be allocated). 311 * @param data the data buffer (<code>null</code> not permitted). 312 * 313 * @return The pixel sample values. 314 * 315 * @throws NullPointerException if <code>data</code> is <code>null</code>. 316 */ 317 public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) 318 { 319 int offset = scanlineStride*y + x; 320 if (iArray == null) iArray = new int[numBands]; 321 int samples = data.getElem(offset); 322 323 for (int b = 0; b < numBands; b++) 324 iArray[b] = (samples & bitMasks[b]) >>> bitOffsets[b]; 325 326 return iArray; 327 } 328 329 /** 330 * Returns an array containing the samples for the pixels in the region 331 * specified by (x, y, w, h) in the specified data buffer. The array is 332 * ordered by pixels (that is, all the samples for the first pixel are 333 * grouped together, followed by all the samples for the second pixel, and so 334 * on). If <code>iArray</code> is not <code>null</code>, it will be 335 * populated with the sample values and returned as the result of this 336 * function (this avoids allocating a new array instance). 337 * 338 * @param x the x-coordinate of the top-left pixel. 339 * @param y the y-coordinate of the top-left pixel. 340 * @param w the width of the region of pixels. 341 * @param h the height of the region of pixels. 342 * @param iArray an array to populate with the sample values and return as 343 * the result (if <code>null</code>, a new array will be allocated). 344 * @param data the data buffer (<code>null</code> not permitted). 345 * 346 * @return The pixel sample values. 347 * 348 * @throws NullPointerException if <code>data</code> is <code>null</code>. 349 */ 350 public int[] getPixels(int x, int y, int w, int h, int[] iArray, 351 DataBuffer data) 352 { 353 int offset = scanlineStride*y + x; 354 if (iArray == null) iArray = new int[numBands*w*h]; 355 int outOffset = 0; 356 for (y = 0; y < h; y++) 357 { 358 int lineOffset = offset; 359 for (x = 0; x < w; x++) 360 { 361 int samples = data.getElem(lineOffset++); 362 for (int b = 0; b < numBands; b++) 363 iArray[outOffset++] = (samples & bitMasks[b]) >>> bitOffsets[b]; 364 } 365 offset += scanlineStride; 366 } 367 return iArray; 368 } 369 370 /** 371 * Returns the sample value for the pixel at (x, y) in the specified data 372 * buffer. 373 * 374 * @param x the x-coordinate of the pixel. 375 * @param y the y-coordinate of the pixel. 376 * @param b the band (in the range <code>0</code> to 377 * <code>getNumBands() - 1</code>). 378 * @param data the data buffer (<code>null</code> not permitted). 379 * 380 * @return The sample value. 381 * 382 * @throws NullPointerException if <code>data</code> is <code>null</code>. 383 */ 384 public int getSample(int x, int y, int b, DataBuffer data) 385 { 386 int offset = scanlineStride*y + x; 387 int samples = data.getElem(offset); 388 return (samples & bitMasks[b]) >>> bitOffsets[b]; 389 } 390 391 public void setDataElements(int x, int y, Object obj, DataBuffer data) 392 { 393 int transferType = getTransferType(); 394 switch (transferType) 395 { 396 case DataBuffer.TYPE_BYTE: 397 { 398 byte[] in = (byte[]) obj; 399 data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xff); 400 } 401 break; 402 case DataBuffer.TYPE_USHORT: 403 { 404 short[] in = (short[]) obj; 405 data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xffff); 406 } 407 break; 408 case DataBuffer.TYPE_INT: 409 { 410 int[] in = (int[]) obj; 411 data.setElem(y * scanlineStride + x, in[0]); 412 break; 413 } 414 } 415 } 416 417 /** 418 * Sets the samples for the pixel at (x, y) in the specified data buffer to 419 * the specified values. 420 * 421 * @param x the x-coordinate of the pixel. 422 * @param y the y-coordinate of the pixel. 423 * @param iArray the sample values (<code>null</code> not permitted). 424 * @param data the data buffer (<code>null</code> not permitted). 425 * 426 * @throws NullPointerException if either <code>iArray</code> or 427 * <code>data</code> is <code>null</code>. 428 */ 429 public void setPixel(int x, int y, int[] iArray, DataBuffer data) 430 { 431 int offset = scanlineStride*y + x; 432 433 int samples = 0; 434 for (int b = 0; b < numBands; b++) 435 samples |= (iArray[b] << bitOffsets[b]) & bitMasks[b]; 436 437 data.setElem(offset, samples); 438 } 439 440 /** 441 * This method implements a more efficient way to set pixels than the default 442 * implementation of the super class. It copies the pixel components directly 443 * from the input array instead of creating a intermediate buffer. 444 * @param x The x-coordinate of the pixel rectangle in <code>obj</code>. 445 * @param y The y-coordinate of the pixel rectangle in <code>obj</code>. 446 * @param w The width of the pixel rectangle in <code>obj</code>. 447 * @param h The height of the pixel rectangle in <code>obj</code>. 448 * @param iArray The primitive array containing the pixels to set. 449 * @param data The DataBuffer to store the pixels into. 450 * @see java.awt.image.SampleModel#setPixels(int, int, int, int, int[], 451 * java.awt.image.DataBuffer) 452 */ 453 public void setPixels(int x, int y, int w, int h, int[] iArray, 454 DataBuffer data) 455 { 456 int inOffset = 0; 457 for (int yy=y; yy<(y+h); yy++) 458 { 459 int offset = scanlineStride*yy + x; 460 for (int xx=x; xx<(x+w); xx++) 461 { 462 int samples = 0; 463 for (int b = 0; b < numBands; b++) 464 samples |= (iArray[inOffset+b] << bitOffsets[b]) & bitMasks[b]; 465 data.setElem(0, offset, samples); 466 inOffset += numBands; 467 offset += 1; 468 } 469 } 470 } 471 472 /** 473 * Sets the sample value for a band for the pixel at (x, y) in the 474 * specified data buffer. 475 * 476 * @param x the x-coordinate of the pixel. 477 * @param y the y-coordinate of the pixel. 478 * @param b the band (in the range <code>0</code> to 479 * <code>getNumBands() - 1</code>). 480 * @param s the sample value. 481 * @param data the data buffer (<code>null</code> not permitted). 482 * 483 * @throws NullPointerException if <code>data</code> is <code>null</code>. 484 */ 485 public void setSample(int x, int y, int b, int s, DataBuffer data) 486 { 487 int offset = scanlineStride*y + x; 488 int samples = data.getElem(offset); 489 int bitMask = bitMasks[b]; 490 samples &= ~bitMask; 491 samples |= (s << bitOffsets[b]) & bitMask; 492 data.setElem(offset, samples); 493 } 494 495 /** 496 * Tests this sample model for equality with an arbitrary object. This 497 * method returns <code>true</code> if and only if: 498 * <ul> 499 * <li><code>obj</code> is not <code>null</code>; 500 * <li><code>obj</code> is an instance of 501 * <code>SinglePixelPackedSampleModel</code>; 502 * <li>both models have the same: 503 * <ul> 504 * <li><code>dataType</code>; 505 * <li><code>width</code>; 506 * <li><code>height</code>; 507 * <li><code>numBands</code>; 508 * <li><code>scanlineStride</code>; 509 * <li><code>bitMasks</code>; 510 * <li><code>bitOffsets</code>. 511 * </ul> 512 * </li> 513 * </ul> 514 * 515 * @param obj the object (<code>null</code> permitted) 516 * 517 * @return <code>true</code> if this model is equal to <code>obj</code>, and 518 * <code>false</code> otherwise. 519 */ 520 public boolean equals(Object obj) 521 { 522 if (this == obj) 523 return true; 524 if (! (obj instanceof SinglePixelPackedSampleModel)) 525 return false; 526 SinglePixelPackedSampleModel that = (SinglePixelPackedSampleModel) obj; 527 if (this.dataType != that.dataType) 528 return false; 529 if (this.width != that.width) 530 return false; 531 if (this.height != that.height) 532 return false; 533 if (this.numBands != that.numBands) 534 return false; 535 if (this.scanlineStride != that.scanlineStride) 536 return false; 537 if (!Arrays.equals(this.bitMasks, that.bitMasks)) 538 return false; 539 if (!Arrays.equals(this.bitOffsets, that.bitOffsets)) 540 return false; 541 return true; 542 } 543 544 /** 545 * Returns a hash code for this <code>SinglePixelPackedSampleModel</code>. 546 * 547 * @return A hash code. 548 */ 549 public int hashCode() 550 { 551 // this hash code won't match Sun's, but that shouldn't matter... 552 int result = 193; 553 result = 37 * result + dataType; 554 result = 37 * result + width; 555 result = 37 * result + height; 556 result = 37 * result + numBands; 557 result = 37 * result + scanlineStride; 558 for (int i = 0; i < bitMasks.length; i++) 559 result = 37 * result + bitMasks[i]; 560 for (int i = 0; i < bitOffsets.length; i++) 561 result = 37 * result + bitOffsets[i]; 562 return result; 563 } 564 565 /** 566 * Creates a String with some information about this SampleModel. 567 * @return A String describing this SampleModel. 568 * @see java.lang.Object#toString() 569 */ 570 public String toString() 571 { 572 StringBuffer result = new StringBuffer(); 573 result.append(getClass().getName()); 574 result.append("["); 575 result.append("scanlineStride=").append(scanlineStride); 576 for(int i = 0; i < bitMasks.length; i+=1) 577 { 578 result.append(", mask[").append(i).append("]=0x").append( 579 Integer.toHexString(bitMasks[i])); 580 } 581 582 result.append("]"); 583 return result.toString(); 584 } 585 }