GNU Classpath (0.95) | |
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: 41: /** 42: * MultiPixelPackedSampleModel provides a single band model that supports 43: * multiple pixels in a single unit. Pixels have 2^n bits and 2^k pixels fit 44: * per data element. 45: * 46: * @author Jerry Quinn (jlquinn@optonline.net) 47: */ 48: public class MultiPixelPackedSampleModel extends SampleModel 49: { 50: private int scanlineStride; 51: private int[] bitMasks; 52: private int[] bitOffsets; 53: private int[] sampleSize; 54: private int dataBitOffset; 55: private int elemBits; 56: private int numberOfBits; 57: private int numElems; 58: 59: /** 60: * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified 61: * data type, which should be one of: 62: * <ul> 63: * <li>{@link DataBuffer#TYPE_BYTE};</li> 64: * <li>{@link DataBuffer#TYPE_USHORT};</li> 65: * <li>{@link DataBuffer#TYPE_INT};</li> 66: * </ul> 67: * 68: * @param dataType the data type. 69: * @param w the width (in pixels). 70: * @param h the height (in pixels). 71: * @param numberOfBits the number of bits per pixel (must be a power of 2). 72: */ 73: public MultiPixelPackedSampleModel(int dataType, int w, int h, 74: int numberOfBits) 75: { 76: this(dataType, w, h, numberOfBits, 0, 0); 77: } 78: 79: /** 80: * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified 81: * data type, which should be one of: 82: * <ul> 83: * <li>{@link DataBuffer#TYPE_BYTE};</li> 84: * <li>{@link DataBuffer#TYPE_USHORT};</li> 85: * <li>{@link DataBuffer#TYPE_INT};</li> 86: * </ul> 87: * 88: * @param dataType the data type. 89: * @param w the width (in pixels). 90: * @param h the height (in pixels). 91: * @param numberOfBits the number of bits per pixel (must be a power of 2). 92: * @param scanlineStride the number of data elements from a pixel on one 93: * row to the corresponding pixel in the next row. 94: * @param dataBitOffset the offset to the first data bit. 95: */ 96: public MultiPixelPackedSampleModel(int dataType, int w, int h, 97: int numberOfBits, int scanlineStride, 98: int dataBitOffset) 99: { 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: }
GNU Classpath (0.95) |