Frames | No Frames |
1: /* Copyright (C) 2004, 2006, Free Software Foundation 2: 3: This file is part of GNU Classpath. 4: 5: GNU Classpath is free software; you can redistribute it and/or modify 6: it under the terms of the GNU General Public License as published by 7: the Free Software Foundation; either version 2, or (at your option) 8: any later version. 9: 10: GNU Classpath is distributed in the hope that it will be useful, but 11: WITHOUT ANY WARRANTY; without even the implied warranty of 12: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13: General Public License for more details. 14: 15: You should have received a copy of the GNU General Public License 16: along with GNU Classpath; see the file COPYING. If not, write to the 17: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 18: 02110-1301 USA. 19: 20: Linking this library statically or dynamically with other modules is 21: making a combined work based on this library. Thus, the terms and 22: conditions of the GNU General Public License cover the whole 23: combination. 24: 25: As a special exception, the copyright holders of this library give you 26: permission to link this library with independent modules to produce an 27: executable, regardless of the license terms of these independent 28: modules, and to copy and distribute the resulting executable under 29: terms of your choice, provided that you also meet, for each linked 30: independent module, the terms and conditions of the license of that 31: module. An independent module is a module which is not derived from 32: or based on this library. If you modify this library, you may extend 33: this exception to your version of the library, but you are not 34: obligated to do so. If you do not wish to do so, delete this 35: exception statement from your version. */ 36: 37: package java.awt.image; 38: 39: import gnu.java.awt.Buffers; 40: import gnu.java.lang.CPStringBuilder; 41: 42: /** 43: * MultiPixelPackedSampleModel provides a single band model that supports 44: * multiple pixels in a single unit. Pixels have 2^n bits and 2^k pixels fit 45: * per data element. 46: * 47: * @author Jerry Quinn (jlquinn@optonline.net) 48: */ 49: public class MultiPixelPackedSampleModel extends SampleModel 50: { 51: private int scanlineStride; 52: private int[] bitMasks; 53: private int[] bitOffsets; 54: private int[] sampleSize; 55: private int dataBitOffset; 56: private int elemBits; 57: private int numberOfBits; 58: private int numElems; 59: 60: /** 61: * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified 62: * data type, which should be one of: 63: * <ul> 64: * <li>{@link DataBuffer#TYPE_BYTE};</li> 65: * <li>{@link DataBuffer#TYPE_USHORT};</li> 66: * <li>{@link DataBuffer#TYPE_INT};</li> 67: * </ul> 68: * 69: * @param dataType the data type. 70: * @param w the width (in pixels). 71: * @param h the height (in pixels). 72: * @param numberOfBits the number of bits per pixel (must be a power of 2). 73: */ 74: public MultiPixelPackedSampleModel(int dataType, int w, int h, 75: int numberOfBits) 76: { 77: this(dataType, w, h, numberOfBits, 0, 0); 78: } 79: 80: /** 81: * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified 82: * data type, which should be one of: 83: * <ul> 84: * <li>{@link DataBuffer#TYPE_BYTE};</li> 85: * <li>{@link DataBuffer#TYPE_USHORT};</li> 86: * <li>{@link DataBuffer#TYPE_INT};</li> 87: * </ul> 88: * 89: * @param dataType the data type. 90: * @param w the width (in pixels). 91: * @param h the height (in pixels). 92: * @param numberOfBits the number of bits per pixel (must be a power of 2). 93: * @param scanlineStride the number of data elements from a pixel on one 94: * row to the corresponding pixel in the next row. 95: * @param dataBitOffset the offset to the first data bit. 96: */ 97: public MultiPixelPackedSampleModel(int dataType, int w, int h, 98: int numberOfBits, int scanlineStride, 99: 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: }