1. /*
  2. * @(#)PackedColorModel.java 1.37 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt.image;
  8. import java.awt.Transparency;
  9. import java.awt.color.ColorSpace;
  10. /**
  11. * The <code>PackedColorModel</code> class is an abstract
  12. * {@link ColorModel} class that works with pixel values which represent
  13. * color and alpha information as separate samples and which pack all
  14. * samples for a single pixel into a single int, short, or byte quantity.
  15. * This class can be used with an arbitrary {@link ColorSpace}. The number of
  16. * color samples in the pixel values must be the same as the number of color
  17. * components in the <code>ColorSpace</code>. There can be a single alpha
  18. * sample. The array length is always 1 for those methods that use a
  19. * primitive array pixel representation of type <code>transferType</code>.
  20. * The transfer types supported are DataBuffer.TYPE_BYTE,
  21. * DataBuffer.TYPE_USHORT, and DataBuffer.TYPE_INT.
  22. * Color and alpha samples are stored in the single element of the array
  23. * in bits indicated by bit masks. Each bit mask must be contiguous and
  24. * masks must not overlap. The same masks apply to the single int
  25. * pixel representation used by other methods. The correspondence of
  26. * masks and color/alpha samples is as follows:
  27. * <ul>
  28. * <li> Masks are identified by indices running from 0 through
  29. * {@link ColorModel#getNumComponents() getNumComponents} - 1.
  30. * <li> The first
  31. * {@link ColorModel#getNumColorComponents() getNumColorComponents}
  32. * indices refer to color samples.
  33. * <li> If an alpha sample is present, it corresponds the last index.
  34. * <li> The order of the color indices is specified
  35. * by the <code>ColorSpace</code>. Typically, this reflects the name of
  36. * the color space type (for example, TYPE_RGB), index 0
  37. * corresponds to red, index 1 to green, and index 2 to blue.
  38. * </ul>
  39. * <p>
  40. * The translation from pixel values to color/alpha components for
  41. * display or processing purposes is a one-to-one correspondence of
  42. * samples to components.
  43. * A <code>PackedColorModel</code> is typically used with image data
  44. * that uses masks to define packed samples. For example, a
  45. * <code>PackedColorModel</code> can be used in conjunction with a
  46. * {@link SinglePixelPackedSampleModel} to construct a
  47. * {@link BufferedImage}. Normally the masks used by the
  48. * {@link SampleModel} and the <code>ColorModel</code> would be the same.
  49. * However, if they are different, the color interpretation of pixel data is
  50. * done according to the masks of the <code>ColorModel</code>.
  51. * <p>
  52. * A single <code>int</code> pixel representation is valid for all objects
  53. * of this class since it is always possible to represent pixel values
  54. * used with this class in a single <code>int</code>. Therefore, methods
  55. * that use this representation do not throw an
  56. * <code>IllegalArgumentException</code> due to an invalid pixel value.
  57. * <p>
  58. * A subclass of <code>PackedColorModel</code> is {@link DirectColorModel},
  59. * which is similar to an X11 TrueColor visual.
  60. *
  61. * @see DirectColorModel
  62. * @see SinglePixelPackedSampleModel
  63. * @see BufferedImage
  64. * @version 10 Feb 1997
  65. */
  66. public abstract class PackedColorModel extends ColorModel {
  67. int[] maskArray;
  68. int[] maskOffsets;
  69. float[] scaleFactors;
  70. /**
  71. * Constructs a <code>PackedColorModel</code> from a color mask array,
  72. * which specifies which bits in an <code>int</code> pixel representation
  73. * contain each of the color samples, and an alpha mask. Color
  74. * components are in the specified <code>ColorSpace</code>. The length of
  75. * <code>colorMaskArray</code> should be the number of components in
  76. * the <code>ColorSpace</code>. All of the bits in each mask
  77. * must be contiguous and fit in the specified number of least significant
  78. * bits of an <code>int</code> pixel representation. If the
  79. * <code>alphaMask</code> is 0, there is no alpha. If there is alpha,
  80. * the <code>boolean</code> <code>isAlphaPremultiplied</code> specifies
  81. * how to interpret color and alpha samples in pixel values. If the
  82. * <code>boolean</code> is <code>true</code>, color samples are assumed
  83. * to have been multiplied by the alpha sample. The transparency,
  84. * <code>trans</code>, specifies what alpha values can be represented
  85. * by this color model. The transfer type is the type of primitive
  86. * array used to represent pixel values.
  87. * @param space the specified <code>ColorSpace</code>
  88. * @param bits the number of bits in the pixel values
  89. * @param colorMaskArray array that specifies the masks representing
  90. * the bits of the pixel values that represent the color
  91. * components
  92. * @param alphaMask specifies the mask representing
  93. * the bits of the pixel values that represent the alpha
  94. * component
  95. * @param isAlphaPremultiplied <code>true</code> if color samples are
  96. * premultiplied by the alpha sample; <code>false</code> otherwise
  97. * @param trans specifies the alpha value that can be represented by
  98. * this color model
  99. * @param transferType the type of array used to represent pixel values
  100. * @throws IllegalArgumentException if <code>bits</code> is less than
  101. * 1 or greater than 32
  102. */
  103. public PackedColorModel (ColorSpace space, int bits,
  104. int[] colorMaskArray, int alphaMask,
  105. boolean isAlphaPremultiplied,
  106. int trans, int transferType) {
  107. super(bits, PackedColorModel.createBitsArray(colorMaskArray,
  108. alphaMask),
  109. space, (alphaMask == 0 ? false : true),
  110. isAlphaPremultiplied, trans, transferType);
  111. if (bits < 1 || bits > 32) {
  112. throw new IllegalArgumentException("Number of bits must be between"
  113. +" 1 and 32.");
  114. }
  115. maskArray = new int[numComponents];
  116. maskOffsets = new int[numComponents];
  117. scaleFactors = new float[numComponents];
  118. for (int i=0; i < numColorComponents; i++) {
  119. // Get the mask offset and #bits
  120. DecomposeMask(colorMaskArray[i], i, space.getName(i));
  121. }
  122. if (alphaMask != 0) {
  123. DecomposeMask(alphaMask, numColorComponents, "alpha");
  124. if (nBits[numComponents-1] == 1) {
  125. transparency = Transparency.BITMASK;
  126. }
  127. }
  128. }
  129. /**
  130. * Constructs a <code>PackedColorModel</code> from the specified
  131. * masks which indicate which bits in an <code>int</code> pixel
  132. * representation contain the alpha, red, green and blue color samples.
  133. * Color components are in the specified <code>ColorSpace</code>, which
  134. * must be of type ColorSpace.TYPE_RGB. All of the bits in each
  135. * mask must be contiguous and fit in the specified number of
  136. * least significant bits of an <code>int</code> pixel representation. If
  137. * <code>amask</code> is 0, there is no alpha. If there is alpha,
  138. * the <code>boolean</code> <code>isAlphaPremultiplied</code>
  139. * specifies how to interpret color and alpha samples
  140. * in pixel values. If the <code>boolean</code> is <code>true</code>,
  141. * color samples are assumed to have been multiplied by the alpha sample.
  142. * The transparency, <code>trans</code>, specifies what alpha values
  143. * can be represented by this color model.
  144. * The transfer type is the type of primitive array used to represent
  145. * pixel values.
  146. * @param space the specified <code>ColorSpace</code>
  147. * @param bits the number of bits in the pixel values
  148. * @param rmask specifies the mask representing
  149. * the bits of the pixel values that represent the red
  150. * color component
  151. * @param gmask specifies the mask representing
  152. * the bits of the pixel values that represent the green
  153. * color component
  154. * @param bmask specifies the mask representing
  155. * the bits of the pixel values that represent
  156. * the blue color component
  157. * @param amask specifies the mask representing
  158. * the bits of the pixel values that represent
  159. * the alpha component
  160. * @param isAlphaPremultiplied <code>true</code> if color samples are
  161. * premultiplied by the alpha sample; <code>false</code> otherwise
  162. * @param trans specifies the alpha value that can be represented by
  163. * this color model
  164. * @param transferType the type of array used to represent pixel values
  165. * @throws IllegalArgumentException if <code>space</code> is not a
  166. * TYPE_RGB space
  167. * @see ColorSpace
  168. */
  169. public PackedColorModel(ColorSpace space, int bits, int rmask, int gmask,
  170. int bmask, int amask,
  171. boolean isAlphaPremultiplied,
  172. int trans, int transferType) {
  173. super (bits, PackedColorModel.createBitsArray(rmask, gmask, bmask,
  174. amask),
  175. space, (amask == 0 ? false : true),
  176. isAlphaPremultiplied, trans, transferType);
  177. if (space.getType() != ColorSpace.TYPE_RGB) {
  178. throw new IllegalArgumentException("ColorSpace must be TYPE_RGB.");
  179. }
  180. maskArray = new int[numComponents];
  181. maskOffsets = new int[numComponents];
  182. scaleFactors = new float[numComponents];
  183. DecomposeMask(rmask, 0, "red");
  184. DecomposeMask(gmask, 1, "green");
  185. DecomposeMask(bmask, 2, "blue");
  186. if (amask != 0) {
  187. DecomposeMask(amask, 3, "alpha");
  188. if (nBits[3] == 1) {
  189. transparency = Transparency.BITMASK;
  190. }
  191. }
  192. }
  193. /**
  194. * Returns the mask indicating which bits in a pixel
  195. * contain the specified color/alpha sample. For color
  196. * samples, <code>index</code> corresponds to the placement of color
  197. * sample names in the color space. Thus, an <code>index</code>
  198. * equal to 0 for a CMYK ColorSpace would correspond to
  199. * Cyan and an <code>index</code> equal to 1 would correspond to
  200. * Magenta. If there is alpha, the alpha <code>index</code> would be:
  201. * <pre>
  202. * alphaIndex = numComponents() - 1;
  203. * </pre>
  204. * @param index the specified color or alpha sample
  205. * @return the mask, which indicates which bits of the <code>int</code>
  206. * pixel representation contain the color or alpha sample specified
  207. * by <code>index</code>.
  208. * @throws ArrayIndexOutOfBoundsException if <code>index</code> is
  209. * greater than the number of components minus 1 in this
  210. * <code>PackedColorModel</code> or if <code>index</code> is
  211. * less than zero
  212. */
  213. final public int getMask(int index) {
  214. return maskArray[index];
  215. }
  216. /**
  217. * Returns a mask array indicating which bits in a pixel
  218. * contain the color and alpha samples.
  219. * @return the mask array , which indicates which bits of the
  220. * <code>int</code> pixel
  221. * representation contain the color or alpha samples.
  222. */
  223. final public int[] getMasks() {
  224. return (int[]) maskArray.clone();
  225. }
  226. /*
  227. * A utility function to compute the mask offset and scalefactor,
  228. * store these and the mask in instance arrays, and verify that
  229. * the mask fits in the specified pixel size.
  230. */
  231. private void DecomposeMask(int mask, int idx, String componentName) {
  232. int off = 0;
  233. int count = nBits[idx];
  234. // Store the mask
  235. maskArray[idx] = mask;
  236. // Now find the shift
  237. if (mask != 0) {
  238. while ((mask & 1) == 0) {
  239. mask >>>= 1;
  240. off++;
  241. }
  242. }
  243. if (off + count > pixel_bits) {
  244. throw new IllegalArgumentException(componentName + " mask "+
  245. Integer.toHexString(maskArray[idx])+
  246. " overflows pixel (expecting "+
  247. pixel_bits+" bits");
  248. }
  249. maskOffsets[idx] = off;
  250. if (count == 0) {
  251. // High enough to scale any 0-ff value down to 0.0, but not
  252. // high enough to get Infinity when scaling back to pixel bits
  253. scaleFactors[idx] = 256.0f;
  254. } else {
  255. scaleFactors[idx] = 255.0f / ((1 << count) - 1);
  256. }
  257. }
  258. /**
  259. * Creates a <code>SampleModel</code> with the specified width and
  260. * height that has a data layout compatible with this
  261. * <code>ColorModel</code>.
  262. * @param w the width (in pixels) of the region of the image data
  263. * described
  264. * @param h the height (in pixels) of the region of the image data
  265. * described
  266. * @return the newly created <code>SampleModel</code>.
  267. * @throws IllegalArgumentException if <code>w</code> or
  268. * <code>h</code> is not greater than 0
  269. * @see SampleModel
  270. */
  271. public SampleModel createCompatibleSampleModel(int w, int h) {
  272. return new SinglePixelPackedSampleModel(transferType, w, h,
  273. maskArray);
  274. }
  275. /**
  276. * Checks if the specified <code>SampleModel</code> is compatible
  277. * with this <code>ColorModel</code>. If <code>sm</code> is
  278. * <code>null</code>, this method returns <code>false</code>.
  279. * @param sm the specified <code>SampleModel</code>,
  280. * or <code>null</code>
  281. * @return <code>true</code> if the specified <code>SampleModel</code>
  282. * is compatible with this <code>ColorModel</code>
  283. * <code>false</code> otherwise.
  284. * @see SampleModel
  285. */
  286. public boolean isCompatibleSampleModel(SampleModel sm) {
  287. if (! (sm instanceof SinglePixelPackedSampleModel)) {
  288. return false;
  289. }
  290. // Must have the same number of components
  291. if (numComponents != sm.getNumBands()) {
  292. return false;
  293. }
  294. // Transfer type must be the same
  295. if (sm.getTransferType() != transferType) {
  296. return false;
  297. }
  298. SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
  299. // Now compare the specific masks
  300. int[] bitMasks = sppsm.getBitMasks();
  301. if (bitMasks.length != maskArray.length) {
  302. return false;
  303. }
  304. for (int i=0; i < bitMasks.length; i++) {
  305. if (bitMasks[i] != maskArray[i]) {
  306. return false;
  307. }
  308. }
  309. return true;
  310. }
  311. /**
  312. * Returns a {@link WritableRaster} representing the alpha channel of
  313. * an image, extracted from the input <code>WritableRaster</code>.
  314. * This method assumes that <code>WritableRaster</code> objects
  315. * associated with this <code>ColorModel</code> store the alpha band,
  316. * if present, as the last band of image data. Returns <code>null</code>
  317. * if there is no separate spatial alpha channel associated with this
  318. * <code>ColorModel</code>. This method creates a new
  319. * <code>WritableRaster</code>, but shares the data array.
  320. * @param raster a <code>WritableRaster</code> containing an image
  321. * @return a <code>WritableRaster</code> that represents the alpha
  322. * channel of the image contained in <code>raster</code>.
  323. */
  324. public WritableRaster getAlphaRaster(WritableRaster raster) {
  325. if (hasAlpha() == false) {
  326. return null;
  327. }
  328. int x = raster.getMinX();
  329. int y = raster.getMinY();
  330. int[] band = new int[1];
  331. band[0] = raster.getNumBands() - 1;
  332. return raster.createWritableChild(x, y, raster.getWidth(),
  333. raster.getHeight(), x, y,
  334. band);
  335. }
  336. /**
  337. * Tests if the specified <code>Object</code> is an instance
  338. * of <code>PackedColorModel</code> and equals this
  339. * <code>PackedColorModel</code>.
  340. * @param obj the <code>Object</code> to test for equality
  341. * @return <code>true</code> if the specified <code>Object</code>
  342. * is an instance of <code>PackedColorModel</code> and equals this
  343. * <code>PackedColorModel</code> <code>false</code> otherwise.
  344. */
  345. public boolean equals(Object obj) {
  346. if (!(obj instanceof PackedColorModel)) {
  347. return false;
  348. }
  349. if (!super.equals(obj)) {
  350. return false;
  351. }
  352. PackedColorModel cm = (PackedColorModel) obj;
  353. int numC = cm.getNumComponents();
  354. if (numC != numComponents) {
  355. return false;
  356. }
  357. for(int i=0; i < numC; i++) {
  358. if (maskArray[i] != cm.getMask(i)) {
  359. return false;
  360. }
  361. }
  362. return true;
  363. }
  364. private final static int[] createBitsArray(int[]colorMaskArray,
  365. int alphaMask) {
  366. int numColors = colorMaskArray.length;
  367. int numAlpha = (alphaMask == 0 ? 0 : 1);
  368. int[] arr = new int[numColors+numAlpha];
  369. for (int i=0; i < numColors; i++) {
  370. arr[i] = countBits(colorMaskArray[i]);
  371. if (arr[i] < 0) {
  372. throw new IllegalArgumentException("Noncontiguous color mask ("
  373. + Integer.toHexString(colorMaskArray[i])+
  374. "at index "+i);
  375. }
  376. }
  377. if (alphaMask != 0) {
  378. arr[numColors] = countBits(alphaMask);
  379. if (arr[numColors] < 0) {
  380. throw new IllegalArgumentException("Noncontiguous alpha mask ("
  381. + Integer.toHexString(alphaMask));
  382. }
  383. }
  384. return arr;
  385. }
  386. private final static int[] createBitsArray(int rmask, int gmask, int bmask,
  387. int amask) {
  388. int[] arr = new int[3 + (amask == 0 ? 0 : 1)];
  389. arr[0] = countBits(rmask);
  390. arr[1] = countBits(gmask);
  391. arr[2] = countBits(bmask);
  392. if (arr[0] < 0) {
  393. throw new IllegalArgumentException("Noncontiguous red mask ("
  394. + Integer.toHexString(rmask));
  395. }
  396. else if (arr[1] < 0) {
  397. throw new IllegalArgumentException("Noncontiguous green mask ("
  398. + Integer.toHexString(gmask));
  399. }
  400. else if (arr[2] < 0) {
  401. throw new IllegalArgumentException("Noncontiguous blue mask ("
  402. + Integer.toHexString(bmask));
  403. }
  404. if (amask != 0) {
  405. arr[3] = countBits(amask);
  406. if (arr[3] < 0) {
  407. throw new IllegalArgumentException("Noncontiguous alpha mask ("
  408. + Integer.toHexString(amask));
  409. }
  410. }
  411. return arr;
  412. }
  413. private final static int countBits(int mask) {
  414. int count = 0;
  415. if (mask != 0) {
  416. while ((mask & 1) == 0) {
  417. mask >>>= 1;
  418. }
  419. while ((mask & 1) == 1) {
  420. mask >>>= 1;
  421. count++;
  422. }
  423. }
  424. if (mask != 0) {
  425. return -1;
  426. }
  427. return count;
  428. }
  429. }