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