1. /*
  2. * @(#)MultiPixelPackedSampleModel.java 1.24 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. /* ****************************************************************
  8. ******************************************************************
  9. ******************************************************************
  10. *** COPYRIGHT (c) Eastman Kodak Company, 1997
  11. *** As an unpublished work pursuant to Title 17 of the United
  12. *** States Code. All rights reserved.
  13. ******************************************************************
  14. ******************************************************************
  15. ******************************************************************/
  16. package java.awt.image;
  17. /**
  18. * The <code>MultiPixelPackedSampleModel</code> class represents
  19. * one-banded images and can pack multiple one-sample
  20. * pixels into one data element. Pixels are not allowed to span data elements.
  21. * The data type can be DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT,
  22. * or DataBuffer.TYPE_INT. Each pixel must be a power of 2 number of bits
  23. * and a power of 2 number of pixels must fit exactly in one data element.
  24. * Pixel bit stride is equal to the number of bits per pixel. Scanline
  25. * stride is in data elements and the last several data elements might be
  26. * padded with unused pixels. Data bit offset is the offset in bits from
  27. * the beginning of the {@link DataBuffer} to the first pixel and must be
  28. * a multiple of pixel bit stride.
  29. * <p>
  30. * The following code illustrates extracting the bits for pixel
  31. * <code>x, y</code> from <code>DataBuffer</code> <code>data</code>
  32. * and storing the pixel data in data elements of type
  33. * <code>dataType</code>:
  34. * <pre>
  35. * int dataElementSize = DataBuffer.getDataTypeSize(dataType);
  36. * int bitnum = dataBitOffset + x*pixelBitStride;
  37. * int element = data.getElem(y*scanlineStride + bitnum/dataElementSize);
  38. * int shift = dataElementSize - (bitnum & (dataElementSize-1))
  39. * - pixelBitStride;
  40. * int pixel = (element >> shift) & ((1 << pixelBitStride) - 1);
  41. * </pre>
  42. */
  43. public class MultiPixelPackedSampleModel extends SampleModel
  44. {
  45. /** The number of bits from one pixel to the next. */
  46. int pixelBitStride;
  47. /** Bitmask that extracts the rightmost pixel of a data element. */
  48. int bitMask;
  49. /**
  50. * The number of pixels that fit in a data element. Also used
  51. * as the number of bits per pixel.
  52. */
  53. int pixelsPerDataElement;
  54. /** The size of a data element in bits. */
  55. int dataElementSize;
  56. /** The bit offset into the data array where the first pixel begins.
  57. */
  58. int dataBitOffset;
  59. /** ScanlineStride of the data buffer described in data array elements. */
  60. int scanlineStride;
  61. /**
  62. * Constructs a <code>MultiPixelPackedSampleModel</code> with the
  63. * specified data type, width, height and number of bits per pixel.
  64. * @param dataType the data type for storing samples
  65. * @param w the width, in pixels, of the region of
  66. * image data described
  67. * @param h the height, in pixels, of the region of
  68. * image data described
  69. * @param numberOfBits the number of bits per pixel
  70. */
  71. public MultiPixelPackedSampleModel(int dataType,
  72. int w,
  73. int h,
  74. int numberOfBits) {
  75. this(dataType,w,h,
  76. numberOfBits,
  77. (w*numberOfBits+DataBuffer.getDataTypeSize(dataType)-1)/
  78. DataBuffer.getDataTypeSize(dataType),
  79. 0);
  80. }
  81. /**
  82. * Constructs a <code>MultiPixelPackedSampleModel</code> with
  83. * specified data type, width, height, number of bits per pixel,
  84. * scanline stride and data bit offset.
  85. * @param dataType the data type for storing samples
  86. * @param w the width, in pixels, of the region of
  87. * image data described
  88. * @param h the height, in pixels, of the region of
  89. * image data described
  90. * @param numberOfBits the number of bits per pixel
  91. * @param scanlineStride the line stride of the image data
  92. * @param dataBitOffset the data bit offset for the region of image
  93. * data described
  94. * @exception RasterFormatException if the number of bits per pixel
  95. * is not a power of 2 or if a power of 2 number of
  96. * pixels do not fit in one data element.
  97. */
  98. public MultiPixelPackedSampleModel(int dataType, int w, int h,
  99. int numberOfBits,
  100. int scanlineStride,
  101. int dataBitOffset) {
  102. super(dataType, w, h, 1);
  103. this.dataType = dataType;
  104. this.pixelBitStride = numberOfBits;
  105. this.scanlineStride = scanlineStride;
  106. this.dataBitOffset = dataBitOffset;
  107. this.dataElementSize = DataBuffer.getDataTypeSize(dataType);
  108. this.pixelsPerDataElement = dataElementSizenumberOfBits;
  109. if (pixelsPerDataElement*numberOfBits != dataElementSize) {
  110. throw new RasterFormatException("MultiPixelPackedSampleModel " +
  111. "does not allow pixels to " +
  112. "span data element boundaries");
  113. }
  114. this.bitMask = (1 << numberOfBits) - 1;
  115. }
  116. /**
  117. * Creates a new <code>MultiPixelPackedSampleModel</code> with the
  118. * specified width and height. The new
  119. * <code>MultiPixelPackedSampleModel</code> has the
  120. * same storage data type and number of bits per pixel as this
  121. * <code>MultiPixelPackedSampleModel</code>.
  122. * @param w the specified width
  123. * @param h the specified height
  124. * @return a {@link SampleModel} with the specified width and height
  125. * and with the same storage data type and number of bits per pixel
  126. * as this <code>MultiPixelPackedSampleModel</code>.
  127. */
  128. public SampleModel createCompatibleSampleModel(int w, int h) {
  129. SampleModel sampleModel =
  130. new MultiPixelPackedSampleModel(dataType, w, h, pixelBitStride);
  131. return sampleModel;
  132. }
  133. /**
  134. * Creates a <code>DataBuffer</code> that corresponds to this
  135. * <code>MultiPixelPackedSampleModel</code>. The
  136. * <code>DataBuffer</code> object's data type and size
  137. * is consistent with this <code>MultiPixelPackedSampleModel</code>.
  138. * The <code>DataBuffer</code> has a single bank.
  139. * @return a <code>DataBuffer</code> with the same data type and
  140. * size as this <code>MultiPixelPackedSampleModel</code>.
  141. */
  142. public DataBuffer createDataBuffer() {
  143. DataBuffer dataBuffer = null;
  144. int size = (int)scanlineStride*height;
  145. switch (dataType) {
  146. case DataBuffer.TYPE_BYTE:
  147. dataBuffer = new DataBufferByte(size+(dataBitOffset+7)/8);
  148. break;
  149. case DataBuffer.TYPE_USHORT:
  150. dataBuffer = new DataBufferUShort(size+(dataBitOffset+15)/16);
  151. break;
  152. case DataBuffer.TYPE_INT:
  153. dataBuffer = new DataBufferInt(size+(dataBitOffset+31)/32);
  154. break;
  155. }
  156. return dataBuffer;
  157. }
  158. /**
  159. * Returns the number of data elements needed to transfer one pixel
  160. * via the {@link #getDataElements} and {@link #setDataElements}
  161. * methods. For a <code>MultiPixelPackedSampleModel</code>, this is
  162. * one.
  163. * @return the number of data elements.
  164. */
  165. public int getNumDataElements() {
  166. return 1;
  167. }
  168. /**
  169. * Returns the number of bits per sample for all bands.
  170. * @return the number of bits per sample.
  171. */
  172. public int[] getSampleSize() {
  173. int sampleSize[] = {pixelBitStride};
  174. return sampleSize;
  175. }
  176. /**
  177. * Returns the number of bits per sample for the specified band.
  178. * @param band the specified band
  179. * @return the number of bits per sample for the specified band.
  180. */
  181. public int getSampleSize(int band) {
  182. return pixelBitStride;
  183. }
  184. /**
  185. * Returns the offset of pixel (x, y) in data array elements.
  186. * @param x, y the specified pixel
  187. * @return the offset of the specified pixel.
  188. */
  189. public int getOffset(int x, int y) {
  190. int offset = y * scanlineStride;
  191. offset += (x*pixelBitStride+dataBitOffset)/dataElementSize;
  192. return offset;
  193. }
  194. /**
  195. * Returns the offset, in bits, into the data element in which it is
  196. * stored for the <code>x</code>th pixel of a scanline.
  197. * This offset is the same for all scanlines.
  198. * @param x the specified pixel
  199. * @return the bit offset of the specified pixel.
  200. */
  201. public int getBitOffset(int x){
  202. return (x*pixelBitStride+dataBitOffset)%dataElementSize;
  203. }
  204. /**
  205. * Returns the scanline stride.
  206. * @return the scanline stride of this
  207. * <code>MultiPixelPackedSampleModel</code>.
  208. */
  209. public int getScanlineStride() {
  210. return scanlineStride;
  211. }
  212. /**
  213. * Returns the pixel bit stride in bits. This value is the same as
  214. * the number of bits per pixel.
  215. * @return the <code>pixelBitStride</code> of this
  216. * <code>MultiPixelPackedSampleModel</code>.
  217. */
  218. public int getPixelBitStride() {
  219. return pixelBitStride;
  220. }
  221. /**
  222. * Returns the data bit offset in bits.
  223. * @return the <code>dataBitOffset</code> of this
  224. * <code>MultiPixelPackedSampleModel</code>.
  225. */
  226. public int getDataBitOffset() {
  227. return dataBitOffset;
  228. }
  229. /**
  230. * Returns the TransferType used to transfer pixels by way of the
  231. * <code>getDataElements</code> and <code>setDataElements</code>
  232. * methods. The TransferType might or might not be the same as the
  233. * storage DataType. The TransferType is one of
  234. * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT,
  235. * or DataBuffer.TYPE_INT.
  236. * @return the transfertype.
  237. */
  238. public int getTransferType() {
  239. if (pixelBitStride > 16)
  240. return DataBuffer.TYPE_INT;
  241. else if (pixelBitStride > 8)
  242. return DataBuffer.TYPE_USHORT;
  243. else
  244. return DataBuffer.TYPE_BYTE;
  245. }
  246. /**
  247. * Creates a new <code>MultiPixelPackedSampleModel</code> with a
  248. * subset of the bands of this
  249. * <code>MultiPixelPackedSampleModel</code>. Since a
  250. * <code>MultiPixelPackedSampleModel</code> only has one band, the
  251. * bands argument must have a length of one and indicate the zeroth
  252. * band.
  253. * @param bands the specified bands
  254. * @return a new <code>SampleModel</code> with a subset of bands of
  255. * this <code>MultiPixelPackedSampleModel</code>.
  256. * @exception RasterFormatException if the number of bands requested
  257. * is not one.
  258. */
  259. public SampleModel createSubsetSampleModel(int bands[]) {
  260. if (bands != null) {
  261. if (bands.length != 1)
  262. throw new RasterFormatException("MultiPixelPackedSampleModel has "
  263. + "only one band.");
  264. }
  265. SampleModel sm = createCompatibleSampleModel(width, height);
  266. return sm;
  267. }
  268. /**
  269. * Returns as <code>int</code> the sample in a specified band for the
  270. * pixel located at (x, y). An
  271. * <code>ArrayIndexOutOfBoundsException</code> is thrown if the
  272. * coordinates are not in bounds.
  273. * @param x, y the coordinates of the specified pixel
  274. * @param b the band to return, which is assumed to be 0
  275. * @param data the <code>DataBuffer</code> containing the image
  276. * data
  277. * @return the specified band containing the sample of the specified
  278. * pixel.
  279. * @exception ArrayIndexOutOfBoundException if the specified
  280. * coordinates are not in bounds.
  281. */
  282. public int getSample(int x, int y, int b, DataBuffer data) {
  283. int bitnum = dataBitOffset + x*pixelBitStride;
  284. int element = data.getElem(y*scanlineStride + bitnumdataElementSize);
  285. int shift = dataElementSize - (bitnum & (dataElementSize-1))
  286. - pixelBitStride;
  287. return (element >> shift) & bitMask;
  288. }
  289. /**
  290. * Sets a sample in the specified band for the pixel located at
  291. * (x, y) in the <code>DataBuffer</code> using an
  292. * <code>int</code> for input.
  293. * An <code>ArrayIndexOutOfBoundsException</code> is thrown if the
  294. * coordinates are not in bounds.
  295. * @param x, y the coordinates of the specified pixel
  296. * @param b the band to return, which is assumed to be 0
  297. * @param s the input sample as an <code>int</code>
  298. * @param data the <code>DataBuffer</code> where image data is stored
  299. * @exception ArrayIndexOutOfBoundsException if the coordinates are
  300. * not in bounds.
  301. */
  302. public void setSample(int x, int y, int b, int s,
  303. DataBuffer data) {
  304. int bitnum = dataBitOffset + x * pixelBitStride;
  305. int index = y * scanlineStride + (bitnum / dataElementSize);
  306. int shift = dataElementSize - (bitnum & (dataElementSize-1))
  307. - pixelBitStride;
  308. int element = data.getElem(index);
  309. element &= ~(bitMask << shift);
  310. element |= (s & bitMask) << shift;
  311. data.setElem(index,element);
  312. }
  313. /**
  314. * Returns data for a single pixel in a primitive array of type
  315. * TransferType. For a <code>MultiPixelPackedSampleModel</code>,
  316. * the array has one element, and the type is the smallest of
  317. * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT
  318. * that can hold a single pixel. Generally, <code>obj</code>
  319. * should be passed in as <code>null</code>, so that the
  320. * <code>Object</code> is created automatically and is the
  321. * correct primitive data type.
  322. * <p>
  323. * The following code illustrates transferring data for one pixel from
  324. * <code>DataBuffer</code> <code>db1</code>, whose storage layout is
  325. * described by <code>MultiPixelPackedSampleModel</code>
  326. * <code>mppsm1</code>, to <code>DataBuffer</code> <code>db2</code>,
  327. * whose storage layout is described by
  328. * <code>MultiPixelPackedSampleModel</code> <code>mppsm2</code>.
  329. * The transfer is generally more efficient than using
  330. * <code>getPixel</code> or <code>setPixel</code>.
  331. * <pre>
  332. * MultiPixelPackedSampleModel mppsm1, mppsm2;
  333. * DataBufferInt db1, db2;
  334. * mppsm2.setDataElements(x, y, mppsm1.getDataElements(x, y, null,
  335. * db1), db2);
  336. * </pre>
  337. * Using <code>getDataElements</code> or <code>setDataElements</code>
  338. * to transfer between two <code>DataBuffer/SampleModel</code> pairs
  339. * is legitimate if the <code>SampleModels</code> have the same number
  340. * of bands, corresponding bands have the same number of
  341. * bits per sample, and the TransferTypes are the same.
  342. * <p>
  343. * If <code>obj</code> is not <code>null</code>, it should be a
  344. * primitive array of type TransferType. Otherwise, a
  345. * <code>ClassCastException</code> is thrown. An
  346. * <code>ArrayIndexOutOfBoundsException</code> is thrown if the
  347. * coordinates are not in bounds, or if <code>obj</code> is not
  348. * <code>null</code> and is not large enough to hold the pixel data.
  349. * @param x, y coordinates of the pixel location.
  350. * @param obj a primitive array in which to return the pixel data or
  351. * <code>null</code>.
  352. * @param data the <code>DataBuffer</code> containing the image data.
  353. * @return an <code>Object</code> containing data for the specified
  354. * pixel.
  355. * @exception ClassCastException if <code>obj</code> is not a
  356. * primitive array of type TransferType or is not <code>null</code>
  357. * @exception ArrayIndexOutOfBoundsException if the coordinates are
  358. * not in bounds, or if <code>obj</code> is not <code>null</code> or
  359. * not large enough to hold the pixel data
  360. */
  361. public Object getDataElements(int x, int y, Object obj, DataBuffer data) {
  362. int type = getTransferType();
  363. int bitnum = dataBitOffset + x*pixelBitStride;
  364. int shift = dataElementSize - (bitnum & (dataElementSize-1))
  365. - pixelBitStride;
  366. int element = 0;
  367. switch(type) {
  368. case DataBuffer.TYPE_BYTE:
  369. byte[] bdata;
  370. if (obj == null)
  371. bdata = new byte[1];
  372. else
  373. bdata = (byte[])obj;
  374. element = data.getElem(y*scanlineStride +
  375. bitnumdataElementSize);
  376. bdata[0] = (byte)((element >> shift) & bitMask);
  377. obj = (Object)bdata;
  378. break;
  379. case DataBuffer.TYPE_USHORT:
  380. short[] sdata;
  381. if (obj == null)
  382. sdata = new short[1];
  383. else
  384. sdata = (short[])obj;
  385. element = data.getElem(y*scanlineStride +
  386. bitnumdataElementSize);
  387. sdata[0] = (short)((element >> shift) & bitMask);
  388. obj = (Object)sdata;
  389. break;
  390. case DataBuffer.TYPE_INT:
  391. int[] idata;
  392. if (obj == null)
  393. idata = new int[1];
  394. else
  395. idata = (int[])obj;
  396. element = data.getElem(y*scanlineStride +
  397. bitnumdataElementSize);
  398. idata[0] = (element >> shift) & bitMask;
  399. obj = (Object)idata;
  400. break;
  401. }
  402. return obj;
  403. }
  404. /**
  405. * Returns the specified single band pixel in the first element
  406. * of an <code>int</code> array.
  407. * <code>ArrayIndexOutOfBoundsException</code> is thrown if the
  408. * coordinates are not in bounds.
  409. * @param x, y the coordinates of the pixel location
  410. * @param iArray the array containing the pixel to be returned or
  411. * <code>null</code>
  412. * @param data the <code>DataBuffer</code> where image data is stored
  413. * @return an array containing the specified pixel.
  414. * @exception ArrayIndexOutOfBoundsException if the coordinates
  415. * are not in bounds
  416. */
  417. public int[] getPixel(int x, int y, int iArray[], DataBuffer data) {
  418. int pixels[];
  419. if (iArray != null) {
  420. pixels = iArray;
  421. } else {
  422. pixels = new int [numBands];
  423. }
  424. int bitnum = dataBitOffset + x*pixelBitStride;
  425. int element = data.getElem(y*scanlineStride + bitnumdataElementSize);
  426. int shift = dataElementSize - (bitnum & (dataElementSize-1))
  427. - pixelBitStride;
  428. pixels[0] = (element >> shift) & bitMask;
  429. return pixels;
  430. }
  431. /**
  432. * Sets the data for a single pixel in the specified
  433. * <code>DataBuffer</code> from a primitive array of type
  434. * TransferType. For a <code>MultiPixelPackedSampleModel</code>,
  435. * only the first element of the array holds valid data,
  436. * and the type must be the smallest of
  437. * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, or DataBuffer.TYPE_INT
  438. * that can hold a single pixel.
  439. * <p>
  440. * The following code illustrates transferring data for one pixel from
  441. * <code>DataBuffer</code> <code>db1</code>, whose storage layout is
  442. * described by <code>MultiPixelPackedSampleModel</code>
  443. * <code>mppsm1</code>, to <code>DataBuffer</code> <code>db2</code>,
  444. * whose storage layout is described by
  445. * <code>MultiPixelPackedSampleModel</code> <code>mppsm2</code>.
  446. * The transfer is generally more efficient than using
  447. * <code>getPixel</code> or <code>setPixel</code>.
  448. * <pre>
  449. * MultiPixelPackedSampleModel mppsm1, mppsm2;
  450. * DataBufferInt db1, db2;
  451. * mppsm2.setDataElements(x, y, mppsm1.getDataElements(x, y, null,
  452. * db1), db2);
  453. * </pre>
  454. * Using <code>getDataElements</code> or <code>setDataElements</code> to
  455. * transfer between two <code>DataBuffer/SampleModel</code> pairs is
  456. * legitimate if the <code>SampleModel</code> objects have
  457. * the same number of bands, corresponding bands have the same number of
  458. * bits per sample, and the TransferTypes are the same.
  459. * <p>
  460. * <code>obj</code> must be a primitive array of type TransferType.
  461. * Otherwise, a <code>ClassCastException</code> is thrown. An
  462. * <code>ArrayIndexOutOfBoundsException</code> is thrown if the
  463. * coordinates are not in bounds, or if <code>obj</code> is not large
  464. * enough to hold the pixel data.
  465. * @param x, y the coordinates of the pixel location
  466. * @param obj a primitive array containing pixel data
  467. * @param data the <code>DataBuffer</code> containing the image data
  468. */
  469. public void setDataElements(int x, int y, Object obj, DataBuffer data) {
  470. int type = getTransferType();
  471. int bitnum = dataBitOffset + x * pixelBitStride;
  472. int index = y * scanlineStride + (bitnum / dataElementSize);
  473. int shift = dataElementSize - (bitnum & (dataElementSize-1))
  474. - pixelBitStride;
  475. int element = data.getElem(index);
  476. element &= ~(bitMask << shift);
  477. switch(type) {
  478. case DataBuffer.TYPE_BYTE:
  479. byte[] barray = (byte[])obj;
  480. element |= ( ((int)(barray[0])&0xff) & bitMask) << shift;
  481. data.setElem(index, element);
  482. break;
  483. case DataBuffer.TYPE_USHORT:
  484. short[] sarray = (short[])obj;
  485. element |= ( ((int)(sarray[0])&0xffff) & bitMask) << shift;
  486. data.setElem(index, element);
  487. break;
  488. case DataBuffer.TYPE_INT:
  489. int[] iarray = (int[])obj;
  490. element |= (iarray[0] & bitMask) << shift;
  491. data.setElem(index, element);
  492. break;
  493. }
  494. }
  495. /**
  496. * Sets a pixel in the <code>DataBuffer</code> using an
  497. * <code>int</code> array for input.
  498. * <code>ArrayIndexOutOfBoundsException</code> is thrown if
  499. * the coordinates are not in bounds.
  500. * @param x, y the coordinates of the pixel location
  501. * @param iArray the input pixel in an <code>int</code> array
  502. * @param data the <code>DataBuffer</code> containing the image data
  503. */
  504. public void setPixel(int x, int y, int[] iArray, DataBuffer data) {
  505. int bitnum = dataBitOffset + x * pixelBitStride;
  506. int index = y * scanlineStride + (bitnum / dataElementSize);
  507. int shift = dataElementSize - (bitnum & (dataElementSize-1))
  508. - pixelBitStride;
  509. int element = data.getElem(index);
  510. element &= ~(bitMask << shift);
  511. element |= (iArray[0] & bitMask) << shift;
  512. data.setElem(index,element);
  513. }
  514. }