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