Frames | No Frames |
1: /* Copyright (C) 2000, 2002, 2003, 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 java.util.Arrays; 40: 41: import gnu.java.awt.BitMaskExtent; 42: import gnu.java.awt.Buffers; 43: 44: /** 45: * A <code>SampleModel</code> used when all samples are stored in a single 46: * data element in the {@link DataBuffer}, and each data element contains 47: * samples for one pixel only. 48: * 49: * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) 50: */ 51: public class SinglePixelPackedSampleModel extends SampleModel 52: { 53: private int scanlineStride; 54: private int[] bitMasks; 55: private int[] bitOffsets; 56: private int[] sampleSize; 57: 58: /** 59: * Creates a new <code>SinglePixelPackedSampleModel</code>. 60: * 61: * @param dataType the data buffer type. 62: * @param w the width (in pixels). 63: * @param h the height (in pixels). 64: * @param bitMasks an array containing the bit mask used to extract the 65: * sample value for each band. 66: */ 67: public SinglePixelPackedSampleModel(int dataType, int w, int h, 68: int[] bitMasks) 69: { 70: this(dataType, w, h, w, bitMasks); 71: } 72: 73: /** 74: * Creates a new <code>SinglePixelPackedSampleModel</code>. 75: * 76: * @param dataType the data buffer type. 77: * @param w the width (in pixels). 78: * @param h the height (in pixels). 79: * @param scanlineStride the number of data elements between a pixel on one 80: * row and the corresponding pixel on the next row. 81: * @param bitMasks an array containing the bit mask used to extract the 82: * sample value for each band. 83: */ 84: public SinglePixelPackedSampleModel(int dataType, int w, int h, 85: int scanlineStride, int[] bitMasks) 86: { 87: super(dataType, w, h, bitMasks.length); 88: 89: switch (dataType) 90: { 91: case DataBuffer.TYPE_BYTE: 92: case DataBuffer.TYPE_USHORT: 93: case DataBuffer.TYPE_INT: 94: break; 95: default: 96: throw new IllegalArgumentException( 97: "SinglePixelPackedSampleModel unsupported dataType"); 98: } 99: 100: this.scanlineStride = scanlineStride; 101: this.bitMasks = bitMasks; 102: 103: bitOffsets = new int[numBands]; 104: sampleSize = new int[numBands]; 105: 106: BitMaskExtent extent = new BitMaskExtent(); 107: for (int b = 0; b < numBands; b++) 108: { 109: // the mask is an unsigned integer 110: long mask = bitMasks[b] & 0xFFFFFFFFL; 111: extent.setMask(mask); 112: sampleSize[b] = extent.bitWidth; 113: bitOffsets[b] = extent.leastSignificantBit; 114: } 115: } 116: 117: /** 118: * Returns the number of data elements. 119: * 120: * @return <code>1</code>. 121: */ 122: public int getNumDataElements() 123: { 124: return 1; 125: } 126: 127: /** 128: * Creates a new <code>SampleModel</code> that is compatible with this 129: * model and has the specified width and height. 130: * 131: * @param w the width (in pixels). 132: * @param h the height (in pixels). 133: * 134: * @return The new sample model. 135: */ 136: public SampleModel createCompatibleSampleModel(int w, int h) 137: { 138: /* FIXME: We can avoid recalculation of bit offsets and sample 139: sizes here by passing these from the current instance to a 140: special private constructor. */ 141: return new SinglePixelPackedSampleModel(dataType, w, h, bitMasks); 142: } 143: 144: 145: /** 146: * Creates a DataBuffer for holding pixel data in the format and 147: * layout described by this SampleModel. The returned buffer will 148: * consist of one single bank. 149: * 150: * @return The data buffer. 151: */ 152: public DataBuffer createDataBuffer() 153: { 154: int size; 155: 156: // We can save (scanlineStride - width) pixels at the very end of 157: // the buffer. The Sun reference implementation (J2SE 1.3.1 and 158: // 1.4.1_01) seems to do this; tested with Mauve test code. 159: size = scanlineStride * (height - 1) + width; 160: 161: return Buffers.createBuffer(getDataType(), size); 162: } 163: 164: /** 165: * Returns an array containing the size (in bits) for each band accessed by 166: * the <code>SampleModel</code>. 167: * 168: * @return An array. 169: * 170: * @see #getSampleSize(int) 171: */ 172: public int[] getSampleSize() 173: { 174: return (int[]) sampleSize.clone(); 175: } 176: 177: /** 178: * Returns the size (in bits) of the samples for the specified band. 179: * 180: * @param band the band (in the range <code>0</code> to 181: * <code>getNumBands() - 1</code>). 182: * 183: * @return The sample size (in bits). 184: */ 185: public int getSampleSize(int band) 186: { 187: return sampleSize[band]; 188: } 189: 190: /** 191: * Returns the index in the data buffer that stores the pixel at (x, y). 192: * 193: * @param x the x-coordinate. 194: * @param y the y-coordinate. 195: * 196: * @return The index in the data buffer that stores the pixel at (x, y). 197: */ 198: public int getOffset(int x, int y) 199: { 200: return scanlineStride*y + x; 201: } 202: 203: public int[] getBitOffsets() 204: { 205: return bitOffsets; 206: } 207: 208: public int[] getBitMasks() 209: { 210: return bitMasks; 211: } 212: 213: /** 214: * Returns the number of data elements from a pixel in one row to the 215: * corresponding pixel in the next row. 216: * 217: * @return The scanline stride. 218: */ 219: public int getScanlineStride() 220: { 221: return scanlineStride; 222: } 223: 224: /** 225: * Creates a new <code>SinglePixelPackedSampleModel</code> that accesses 226: * the specified subset of bands. 227: * 228: * @param bands an array containing band indices (<code>null</code> not 229: * permitted). 230: * 231: * @return A new sample model. 232: * 233: * @throws NullPointerException if <code>bands</code> is <code>null</code>. 234: * @throws RasterFormatException if <code>bands.length</code> is greater 235: * than the number of bands in this model. 236: */ 237: public SampleModel createSubsetSampleModel(int[] bands) 238: { 239: if (bands.length > numBands) 240: throw new RasterFormatException("Too many bands."); 241: 242: int numBands = bands.length; 243: 244: int[] bitMasks = new int[numBands]; 245: 246: for (int b = 0; b < numBands; b++) 247: bitMasks[b] = this.bitMasks[bands[b]]; 248: 249: return new SinglePixelPackedSampleModel(dataType, width, height, 250: scanlineStride, bitMasks); 251: } 252: 253: public Object getDataElements(int x, int y, Object obj, 254: DataBuffer data) 255: { 256: int offset = scanlineStride*y + x + data.getOffset(); 257: 258: return Buffers.getData(data, offset, obj, 259: 0, // destination offset, 260: 1 // length 261: ); 262: } 263: 264: /** 265: * This is a more efficient implementation of the default implementation in 266: * the super class. 267: * @param x The x-coordinate of the pixel rectangle to store in 268: * <code>obj</code>. 269: * @param y The y-coordinate of the pixel rectangle to store in 270: * <code>obj</code>. 271: * @param w The width of the pixel rectangle to store in <code>obj</code>. 272: * @param h The height of the pixel rectangle to store in <code>obj</code>. 273: * @param obj The primitive array to store the pixels into or null to force 274: * creation. 275: * @param data The DataBuffer that is the source of the pixel data. 276: * @return The primitive array containing the pixel data. 277: * @see java.awt.image.SampleModel#getDataElements(int, int, int, int, 278: * java.lang.Object, java.awt.image.DataBuffer) 279: */ 280: public Object getDataElements(int x, int y, int w, int h, Object obj, 281: DataBuffer data) 282: { 283: int size = w*h; 284: int dataSize = size; 285: Object pixelData = null; 286: switch (getTransferType()) 287: { 288: case DataBuffer.TYPE_BYTE: 289: pixelData = ((DataBufferByte) data).getData(); 290: if (obj == null) obj = new byte[dataSize]; 291: break; 292: case DataBuffer.TYPE_USHORT: 293: pixelData = ((DataBufferUShort) data).getData(); 294: if (obj == null) obj = new short[dataSize]; 295: break; 296: case DataBuffer.TYPE_INT: 297: pixelData = ((DataBufferInt) data).getData(); 298: if (obj == null) obj = new int[dataSize]; 299: break; 300: default: 301: // Seems like the only sensible thing to do. 302: throw new ClassCastException(); 303: } 304: if(x == 0 && scanlineStride == w) 305: { 306: // The full width need to be copied therefore we can copy in one shot. 307: System.arraycopy(pixelData, scanlineStride*y + data.getOffset(), obj, 308: 0, size); 309: } 310: else 311: { 312: // Since we do not need the full width we need to copy line by line. 313: int outOffset = 0; 314: int dataOffset = scanlineStride*y + x + data.getOffset(); 315: for (int yy = y; yy<(y+h); yy++) 316: { 317: System.arraycopy(pixelData, dataOffset, obj, outOffset, w); 318: dataOffset += scanlineStride; 319: outOffset += w; 320: } 321: } 322: return obj; 323: } 324: 325: /** 326: * Returns an array containing the samples for the pixel at (x, y) in the 327: * specified data buffer. If <code>iArray</code> is not <code>null</code>, 328: * it will be populated with the sample values and returned as the result of 329: * this function (this avoids allocating a new array instance). 330: * 331: * @param x the x-coordinate of the pixel. 332: * @param y the y-coordinate of the pixel. 333: * @param iArray an array to populate with the sample values and return as 334: * the result (if <code>null</code>, a new array will be allocated). 335: * @param data the data buffer (<code>null</code> not permitted). 336: * 337: * @return The pixel sample values. 338: * 339: * @throws NullPointerException if <code>data</code> is <code>null</code>. 340: */ 341: public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) 342: { 343: int offset = scanlineStride*y + x; 344: if (iArray == null) iArray = new int[numBands]; 345: int samples = data.getElem(offset); 346: 347: for (int b = 0; b < numBands; b++) 348: iArray[b] = (samples & bitMasks[b]) >>> bitOffsets[b]; 349: 350: return iArray; 351: } 352: 353: /** 354: * Returns an array containing the samples for the pixels in the region 355: * specified by (x, y, w, h) in the specified data buffer. The array is 356: * ordered by pixels (that is, all the samples for the first pixel are 357: * grouped together, followed by all the samples for the second pixel, and so 358: * on). If <code>iArray</code> is not <code>null</code>, it will be 359: * populated with the sample values and returned as the result of this 360: * function (this avoids allocating a new array instance). 361: * 362: * @param x the x-coordinate of the top-left pixel. 363: * @param y the y-coordinate of the top-left pixel. 364: * @param w the width of the region of pixels. 365: * @param h the height of the region of pixels. 366: * @param iArray an array to populate with the sample values and return as 367: * the result (if <code>null</code>, a new array will be allocated). 368: * @param data the data buffer (<code>null</code> not permitted). 369: * 370: * @return The pixel sample values. 371: * 372: * @throws NullPointerException if <code>data</code> is <code>null</code>. 373: */ 374: public int[] getPixels(int x, int y, int w, int h, int[] iArray, 375: DataBuffer data) 376: { 377: int offset = scanlineStride*y + x; 378: if (iArray == null) iArray = new int[numBands*w*h]; 379: int outOffset = 0; 380: for (y = 0; y < h; y++) 381: { 382: int lineOffset = offset; 383: for (x = 0; x < w; x++) 384: { 385: int samples = data.getElem(lineOffset++); 386: for (int b = 0; b < numBands; b++) 387: iArray[outOffset++] = (samples & bitMasks[b]) >>> bitOffsets[b]; 388: } 389: offset += scanlineStride; 390: } 391: return iArray; 392: } 393: 394: /** 395: * Returns the sample value for the pixel at (x, y) in the specified data 396: * buffer. 397: * 398: * @param x the x-coordinate of the pixel. 399: * @param y the y-coordinate of the pixel. 400: * @param b the band (in the range <code>0</code> to 401: * <code>getNumBands() - 1</code>). 402: * @param data the data buffer (<code>null</code> not permitted). 403: * 404: * @return The sample value. 405: * 406: * @throws NullPointerException if <code>data</code> is <code>null</code>. 407: */ 408: public int getSample(int x, int y, int b, DataBuffer data) 409: { 410: int offset = scanlineStride*y + x; 411: int samples = data.getElem(offset); 412: return (samples & bitMasks[b]) >>> bitOffsets[b]; 413: } 414: 415: /** 416: * This method implements a more efficient way to set data elements than the 417: * default implementation of the super class. It sets the data elements line 418: * by line instead of pixel by pixel. 419: * 420: * @param x The x-coordinate of the data elements in <code>obj</code>. 421: * @param y The y-coordinate of the data elements in <code>obj</code>. 422: * @param w The width of the data elements in <code>obj</code>. 423: * @param h The height of the data elements in <code>obj</code>. 424: * @param obj The primitive array containing the data elements to set. 425: * @param data The DataBuffer to store the data elements into. 426: * @see java.awt.image.SampleModel#setDataElements(int, int, int, int, 427: * java.lang.Object, java.awt.image.DataBuffer) 428: */ 429: public void setDataElements(int x, int y, int w, int h, 430: Object obj, DataBuffer data) 431: { 432: 433: Object pixelData; 434: switch (getTransferType()) 435: { 436: case DataBuffer.TYPE_BYTE: 437: pixelData = ((DataBufferByte) data).getData(); 438: break; 439: case DataBuffer.TYPE_USHORT: 440: pixelData = ((DataBufferUShort) data).getData(); 441: break; 442: case DataBuffer.TYPE_INT: 443: pixelData = ((DataBufferInt) data).getData(); 444: break; 445: default: 446: // Seems like the only sensible thing to do. 447: throw new ClassCastException(); 448: } 449: 450: int inOffset = 0; 451: int dataOffset = scanlineStride*y + x + data.getOffset(); 452: for (int yy=y; yy<(y+h); yy++) 453: { 454: System.arraycopy(obj,inOffset,pixelData,dataOffset,w); 455: dataOffset += scanlineStride; 456: inOffset += w; 457: } 458: } 459: 460: 461: public void setDataElements(int x, int y, Object obj, DataBuffer data) 462: { 463: int offset = scanlineStride*y + x + data.getOffset(); 464: 465: int transferType = getTransferType(); 466: if (getTransferType() != data.getDataType()) 467: { 468: throw new IllegalArgumentException("transfer type ("+ 469: getTransferType()+"), "+ 470: "does not match data "+ 471: "buffer type (" + 472: data.getDataType() + 473: ")."); 474: } 475: 476: try 477: { 478: switch (transferType) 479: { 480: case DataBuffer.TYPE_BYTE: 481: { 482: DataBufferByte out = (DataBufferByte) data; 483: byte[] in = (byte[]) obj; 484: out.getData()[offset] = in[0]; 485: return; 486: } 487: case DataBuffer.TYPE_USHORT: 488: { 489: DataBufferUShort out = (DataBufferUShort) data; 490: short[] in = (short[]) obj; 491: out.getData()[offset] = in[0]; 492: return; 493: } 494: case DataBuffer.TYPE_INT: 495: { 496: DataBufferInt out = (DataBufferInt) data; 497: int[] in = (int[]) obj; 498: out.getData()[offset] = in[0]; 499: return; 500: } 501: // FIXME: Fill in the other possible types. 502: default: 503: throw new InternalError(); 504: } 505: } 506: catch (ArrayIndexOutOfBoundsException aioobe) 507: { 508: String msg = "While writing data elements" + 509: ", x="+x+", y="+y+ 510: ", width="+width+", height="+height+ 511: ", scanlineStride="+scanlineStride+ 512: ", offset="+offset+ 513: ", data.getSize()="+data.getSize()+ 514: ", data.getOffset()="+data.getOffset()+ 515: ": " + 516: aioobe; 517: throw new ArrayIndexOutOfBoundsException(msg); 518: } 519: } 520: 521: /** 522: * Sets the samples for the pixel at (x, y) in the specified data buffer to 523: * the specified values. 524: * 525: * @param x the x-coordinate of the pixel. 526: * @param y the y-coordinate of the pixel. 527: * @param iArray the sample values (<code>null</code> not permitted). 528: * @param data the data buffer (<code>null</code> not permitted). 529: * 530: * @throws NullPointerException if either <code>iArray</code> or 531: * <code>data</code> is <code>null</code>. 532: */ 533: public void setPixel(int x, int y, int[] iArray, DataBuffer data) 534: { 535: int offset = scanlineStride*y + x; 536: 537: int samples = 0; 538: for (int b = 0; b < numBands; b++) 539: samples |= (iArray[b] << bitOffsets[b]) & bitMasks[b]; 540: 541: data.setElem(offset, samples); 542: } 543: 544: /** 545: * This method implements a more efficient way to set pixels than the default 546: * implementation of the super class. It copies the pixel components directly 547: * from the input array instead of creating a intermediate buffer. 548: * @param x The x-coordinate of the pixel rectangle in <code>obj</code>. 549: * @param y The y-coordinate of the pixel rectangle in <code>obj</code>. 550: * @param w The width of the pixel rectangle in <code>obj</code>. 551: * @param h The height of the pixel rectangle in <code>obj</code>. 552: * @param iArray The primitive array containing the pixels to set. 553: * @param data The DataBuffer to store the pixels into. 554: * @see java.awt.image.SampleModel#setPixels(int, int, int, int, int[], 555: * java.awt.image.DataBuffer) 556: */ 557: public void setPixels(int x, int y, int w, int h, int[] iArray, 558: DataBuffer data) 559: { 560: int inOffset = 0; 561: int[] pixel = new int[numBands]; 562: for (int yy=y; yy<(y+h); yy++) 563: { 564: int offset = scanlineStride*yy + x; 565: for (int xx=x; xx<(x+w); xx++) 566: { 567: int samples = 0; 568: for (int b = 0; b < numBands; b++) 569: samples |= (iArray[inOffset+b] << bitOffsets[b]) & bitMasks[b]; 570: data.setElem(0, offset, samples); 571: inOffset += numBands; 572: offset += 1; 573: } 574: } 575: } 576: 577: /** 578: * Sets the sample value for a band for the pixel at (x, y) in the 579: * specified data buffer. 580: * 581: * @param x the x-coordinate of the pixel. 582: * @param y the y-coordinate of the pixel. 583: * @param b the band (in the range <code>0</code> to 584: * <code>getNumBands() - 1</code>). 585: * @param s the sample value. 586: * @param data the data buffer (<code>null</code> not permitted). 587: * 588: * @throws NullPointerException if <code>data</code> is <code>null</code>. 589: */ 590: public void setSample(int x, int y, int b, int s, DataBuffer data) 591: { 592: int offset = scanlineStride*y + x; 593: int samples = data.getElem(offset); 594: int bitMask = bitMasks[b]; 595: samples &= ~bitMask; 596: samples |= (s << bitOffsets[b]) & bitMask; 597: data.setElem(offset, samples); 598: } 599: 600: /** 601: * Tests this sample model for equality with an arbitrary object. This 602: * method returns <code>true</code> if and only if: 603: * <ul> 604: * <li><code>obj</code> is not <code>null</code>; 605: * <li><code>obj</code> is an instance of 606: * <code>SinglePixelPackedSampleModel</code>; 607: * <li>both models have the same: 608: * <ul> 609: * <li><code>dataType</code>; 610: * <li><code>width</code>; 611: * <li><code>height</code>; 612: * <li><code>numBands</code>; 613: * <li><code>scanlineStride</code>; 614: * <li><code>bitMasks</code>; 615: * <li><code>bitOffsets</code>. 616: * </ul> 617: * </li> 618: * </ul> 619: * 620: * @param obj the object (<code>null</code> permitted) 621: * 622: * @return <code>true</code> if this model is equal to <code>obj</code>, and 623: * <code>false</code> otherwise. 624: */ 625: public boolean equals(Object obj) 626: { 627: if (this == obj) 628: return true; 629: if (! (obj instanceof SinglePixelPackedSampleModel)) 630: return false; 631: SinglePixelPackedSampleModel that = (SinglePixelPackedSampleModel) obj; 632: if (this.dataType != that.dataType) 633: return false; 634: if (this.width != that.width) 635: return false; 636: if (this.height != that.height) 637: return false; 638: if (this.numBands != that.numBands) 639: return false; 640: if (this.scanlineStride != that.scanlineStride) 641: return false; 642: if (!Arrays.equals(this.bitMasks, that.bitMasks)) 643: return false; 644: if (!Arrays.equals(this.bitOffsets, that.bitOffsets)) 645: return false; 646: return true; 647: } 648: 649: /** 650: * Returns a hash code for this <code>SinglePixelPackedSampleModel</code>. 651: * 652: * @return A hash code. 653: */ 654: public int hashCode() 655: { 656: // this hash code won't match Sun's, but that shouldn't matter... 657: int result = 193; 658: result = 37 * result + dataType; 659: result = 37 * result + width; 660: result = 37 * result + height; 661: result = 37 * result + numBands; 662: result = 37 * result + scanlineStride; 663: for (int i = 0; i < bitMasks.length; i++) 664: result = 37 * result + bitMasks[i]; 665: for (int i = 0; i < bitOffsets.length; i++) 666: result = 37 * result + bitOffsets[i]; 667: return result; 668: } 669: 670: /** 671: * Creates a String with some information about this SampleModel. 672: * @return A String describing this SampleModel. 673: * @see java.lang.Object#toString() 674: */ 675: public String toString() 676: { 677: StringBuffer result = new StringBuffer(); 678: result.append(getClass().getName()); 679: result.append("["); 680: result.append("scanlineStride=").append(scanlineStride); 681: for(int i = 0; i < bitMasks.length; i+=1) 682: { 683: result.append(", mask[").append(i).append("]=0x").append( 684: Integer.toHexString(bitMasks[i])); 685: } 686: 687: result.append("]"); 688: return result.toString(); 689: } 690: }