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