1. /*
  2. * @(#)ImageTypeSpecifier.java 1.34 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.imageio;
  8. import java.awt.Point;
  9. import java.awt.Transparency;
  10. import java.awt.image.BandedSampleModel;
  11. import java.awt.image.BufferedImage;
  12. import java.awt.image.ColorModel;
  13. import java.awt.color.ColorSpace;
  14. import java.awt.image.IndexColorModel;
  15. import java.awt.image.ComponentColorModel;
  16. import java.awt.image.DataBuffer;
  17. import java.awt.image.DirectColorModel;
  18. import java.awt.image.MultiPixelPackedSampleModel;
  19. import java.awt.image.PixelInterleavedSampleModel;
  20. import java.awt.image.SinglePixelPackedSampleModel;
  21. import java.awt.image.Raster;
  22. import java.awt.image.RenderedImage;
  23. import java.awt.image.SampleModel;
  24. import java.awt.image.WritableRaster;
  25. import java.util.Hashtable;
  26. /**
  27. * A class that allows the format of an image (in particular, its
  28. * <code>SampleModel</code> and <code>ColorModel</code>) to be
  29. * specified in a convenient manner.
  30. *
  31. * @version 0.5
  32. */
  33. public class ImageTypeSpecifier {
  34. /**
  35. * The <code>ColorModel</code> to be used as a prototype.
  36. */
  37. protected ColorModel colorModel;
  38. /**
  39. * A <code>SampleModel</code> to be used as a prototype.
  40. */
  41. protected SampleModel sampleModel;
  42. /**
  43. * Cached specifiers for all of the standard
  44. * <code>BufferedImage</code> types.
  45. */
  46. private static ImageTypeSpecifier[] BISpecifier;
  47. // Initialize the standard specifiers
  48. static {
  49. ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  50. BISpecifier =
  51. new ImageTypeSpecifier[BufferedImage.TYPE_BYTE_INDEXED + 1];
  52. BISpecifier[BufferedImage.TYPE_CUSTOM] = null;
  53. BISpecifier[BufferedImage.TYPE_INT_RGB] =
  54. createPacked(sRGB,
  55. 0x00ff0000,
  56. 0x0000ff00,
  57. 0x000000ff,
  58. 0x0,
  59. DataBuffer.TYPE_INT,
  60. false);
  61. BISpecifier[BufferedImage.TYPE_INT_ARGB] =
  62. createPacked(sRGB,
  63. 0x00ff0000,
  64. 0x0000ff00,
  65. 0x000000ff,
  66. 0xff000000,
  67. DataBuffer.TYPE_INT,
  68. false);
  69. BISpecifier[BufferedImage.TYPE_INT_ARGB_PRE] =
  70. createPacked(sRGB,
  71. 0x00ff0000,
  72. 0x0000ff00,
  73. 0x000000ff,
  74. 0xff000000,
  75. DataBuffer.TYPE_INT,
  76. true);
  77. BISpecifier[BufferedImage.TYPE_INT_BGR] =
  78. createPacked(sRGB,
  79. 0x000000ff,
  80. 0x0000ff00,
  81. 0x00ff0000,
  82. 0x0,
  83. DataBuffer.TYPE_INT,
  84. false);
  85. int[] bOffsRGB = { 2, 1, 0 };
  86. BISpecifier[BufferedImage.TYPE_3BYTE_BGR] =
  87. createInterleaved(sRGB,
  88. bOffsRGB,
  89. DataBuffer.TYPE_BYTE,
  90. false,
  91. false);
  92. int[] bOffsABGR = { 3, 2, 1, 0 };
  93. BISpecifier[BufferedImage.TYPE_4BYTE_ABGR] =
  94. createInterleaved(sRGB,
  95. bOffsABGR,
  96. DataBuffer.TYPE_BYTE,
  97. true,
  98. false);
  99. BISpecifier[BufferedImage.TYPE_4BYTE_ABGR_PRE] =
  100. createInterleaved(sRGB,
  101. bOffsABGR,
  102. DataBuffer.TYPE_BYTE,
  103. true,
  104. true);
  105. BISpecifier[BufferedImage.TYPE_USHORT_565_RGB] =
  106. createPacked(sRGB,
  107. 0xF800,
  108. 0x07E0,
  109. 0x001F,
  110. 0x0,
  111. DataBuffer.TYPE_USHORT,
  112. false);
  113. BISpecifier[BufferedImage.TYPE_USHORT_555_RGB] =
  114. createPacked(sRGB,
  115. 0x7C00,
  116. 0x03E0,
  117. 0x001F,
  118. 0x0,
  119. DataBuffer.TYPE_USHORT,
  120. false);
  121. BISpecifier[BufferedImage.TYPE_BYTE_GRAY] =
  122. createGrayscale(8,
  123. DataBuffer.TYPE_BYTE,
  124. false);
  125. BISpecifier[BufferedImage.TYPE_USHORT_GRAY] =
  126. createGrayscale(16,
  127. DataBuffer.TYPE_USHORT,
  128. false);
  129. BISpecifier[BufferedImage.TYPE_BYTE_BINARY] =
  130. createGrayscale(1,
  131. DataBuffer.TYPE_BYTE,
  132. false);
  133. BufferedImage bi =
  134. new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED);
  135. IndexColorModel icm = (IndexColorModel)bi.getColorModel();
  136. int mapSize = icm.getMapSize();
  137. byte[] redLUT = new byte[mapSize];
  138. byte[] greenLUT = new byte[mapSize];
  139. byte[] blueLUT = new byte[mapSize];
  140. byte[] alphaLUT = new byte[mapSize];
  141. icm.getReds(redLUT);
  142. icm.getGreens(greenLUT);
  143. icm.getBlues(blueLUT);
  144. icm.getAlphas(alphaLUT);
  145. BISpecifier[BufferedImage.TYPE_BYTE_INDEXED] =
  146. createIndexed(redLUT, greenLUT, blueLUT, alphaLUT,
  147. 8,
  148. DataBuffer.TYPE_BYTE);
  149. }
  150. /**
  151. * A constructor to be used by inner subclasses only.
  152. */
  153. private ImageTypeSpecifier() {}
  154. /**
  155. * Constructs an <code>ImageTypeSpecifier</code> directly
  156. * from a <code>ColorModel</code> and a <code>SampleModel</code>.
  157. * It is the caller's responsibility to supply compatible
  158. * parameters.
  159. *
  160. * @param colorModel a <code>ColorModel</code>.
  161. * @param sampleModel a <code>SampleModel</code>.
  162. *
  163. * @exception IllegalArgumentException if either parameter is
  164. * <code>null</code>.
  165. * @exception IllegalArgumentException if <code>sampleModel</code>
  166. * is not compatible with <code>colorModel</code>.
  167. */
  168. public ImageTypeSpecifier(ColorModel colorModel, SampleModel sampleModel) {
  169. if (colorModel == null) {
  170. throw new IllegalArgumentException("colorModel == null!");
  171. }
  172. if (sampleModel == null) {
  173. throw new IllegalArgumentException("sampleModel == null!");
  174. }
  175. if (!colorModel.isCompatibleSampleModel(sampleModel)) {
  176. throw new IllegalArgumentException
  177. ("sampleModel is incompatible with colorModel!");
  178. }
  179. this.colorModel = colorModel;
  180. this.sampleModel = sampleModel;
  181. }
  182. /**
  183. * Constructs an <code>ImageTypeSpecifier</code> from a
  184. * <code>RenderedImage</code>. If a <code>BufferedImage</code> is
  185. * being used, one of the factory methods
  186. * <code>createFromRenderedImage</code> or
  187. * <code>createFromBufferedImageType</code> should be used instead in
  188. * order to get a more accurate result.
  189. *
  190. * @param image a <code>RenderedImage</code>.
  191. *
  192. * @exception IllegalArgumentException if the argument is
  193. * <code>null</code>.
  194. */
  195. public ImageTypeSpecifier(RenderedImage image) {
  196. if (image == null) {
  197. throw new IllegalArgumentException("image == null!");
  198. }
  199. colorModel = image.getColorModel();
  200. sampleModel = image.getSampleModel();
  201. }
  202. // Packed
  203. static class Packed extends ImageTypeSpecifier {
  204. ColorSpace colorSpace;
  205. int redMask;
  206. int greenMask;
  207. int blueMask;
  208. int alphaMask;
  209. int transferType;
  210. boolean isAlphaPremultiplied;
  211. public Packed(ColorSpace colorSpace,
  212. int redMask,
  213. int greenMask,
  214. int blueMask,
  215. int alphaMask, // 0 if no alpha
  216. int transferType,
  217. boolean isAlphaPremultiplied) {
  218. if (colorSpace == null) {
  219. throw new IllegalArgumentException("colorSpace == null!");
  220. }
  221. if (colorSpace.getType() != ColorSpace.TYPE_RGB) {
  222. throw new IllegalArgumentException
  223. ("colorSpace is not of type TYPE_RGB!");
  224. }
  225. if (transferType != DataBuffer.TYPE_BYTE &&
  226. transferType != DataBuffer.TYPE_USHORT &&
  227. transferType != DataBuffer.TYPE_INT) {
  228. throw new IllegalArgumentException
  229. ("Bad value for transferType!");
  230. }
  231. if (redMask == 0 && greenMask == 0 &&
  232. blueMask == 0 && alphaMask == 0) {
  233. throw new IllegalArgumentException
  234. ("No mask has at least 1 bit set!");
  235. }
  236. this.colorSpace = colorSpace;
  237. this.redMask = redMask;
  238. this.greenMask = greenMask;
  239. this.blueMask = blueMask;
  240. this.alphaMask = alphaMask;
  241. this.transferType = transferType;
  242. this.isAlphaPremultiplied = isAlphaPremultiplied;
  243. int bits = 32;
  244. this.colorModel =
  245. new DirectColorModel(colorSpace,
  246. bits,
  247. redMask, greenMask, blueMask,
  248. alphaMask, isAlphaPremultiplied,
  249. transferType);
  250. this.sampleModel = colorModel.createCompatibleSampleModel(1, 1);
  251. }
  252. }
  253. /**
  254. * Returns a specifier for a packed image format that will use a
  255. * <code>DirectColorModel</code> and a packed
  256. * <code>SampleModel</code> to store each pixel packed into in a
  257. * single byte, short, or int.
  258. *
  259. * @param colorSpace the desired <code>ColorSpace</code>.
  260. * @param redMask a contiguous mask indicated the position of the
  261. * red channel.
  262. * @param greenMask a contiguous mask indicated the position of the
  263. * green channel.
  264. * @param blueMask a contiguous mask indicated the position of the
  265. * blue channel.
  266. * @param alphaMask a contiguous mask indicated the position of the
  267. * alpha channel.
  268. * @param transferType the desired <code>SampleModel</code> transfer type.
  269. * @param isAlphaPremultiplied <code>true</code> if the color channels
  270. * will be premultipled by the alpha channel.
  271. *
  272. * @return an <code>ImageTypeSpecifier</code> with the desired
  273. * characteristics.
  274. *
  275. * @exception IllegalArgumentException if <code>colorSpace</code>
  276. * is <code>null</code>.
  277. * @exception IllegalArgumentException if <code>colorSpace</code>
  278. * is not of type <code>TYPE_RGB</code>.
  279. * @exception IllegalArgumentException if no mask has at least 1
  280. * bit set.
  281. * @exception IllegalArgumentException if
  282. * <code>transferType</code> if not one of
  283. * <code>DataBuffer.TYPE_BYTE</code>,
  284. * <code>DataBuffer.TYPE_USHORT</code>, or
  285. * <code>DataBuffer.TYPE_INT</code>.
  286. */
  287. public static ImageTypeSpecifier
  288. createPacked(ColorSpace colorSpace,
  289. int redMask,
  290. int greenMask,
  291. int blueMask,
  292. int alphaMask, // 0 if no alpha
  293. int transferType,
  294. boolean isAlphaPremultiplied) {
  295. return new ImageTypeSpecifier.Packed(colorSpace,
  296. redMask,
  297. greenMask,
  298. blueMask,
  299. alphaMask, // 0 if no alpha
  300. transferType,
  301. isAlphaPremultiplied);
  302. }
  303. static ColorModel createComponentCM(ColorSpace colorSpace,
  304. int numBands,
  305. int dataType,
  306. boolean hasAlpha,
  307. boolean isAlphaPremultiplied) {
  308. int transparency =
  309. hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE;
  310. int[] numBits = new int[numBands];
  311. int bits = DataBuffer.getDataTypeSize(dataType);
  312. for (int i = 0; i < numBands; i++) {
  313. numBits[i] = bits;
  314. }
  315. return new ComponentColorModel(colorSpace,
  316. numBits,
  317. hasAlpha,
  318. isAlphaPremultiplied,
  319. transparency,
  320. dataType);
  321. }
  322. // Interleaved
  323. static class Interleaved extends ImageTypeSpecifier {
  324. ColorSpace colorSpace;
  325. int[] bandOffsets;
  326. int dataType;
  327. boolean hasAlpha;
  328. boolean isAlphaPremultiplied;
  329. public Interleaved(ColorSpace colorSpace,
  330. int[] bandOffsets,
  331. int dataType,
  332. boolean hasAlpha,
  333. boolean isAlphaPremultiplied) {
  334. if (colorSpace == null) {
  335. throw new IllegalArgumentException("colorSpace == null!");
  336. }
  337. if (bandOffsets == null) {
  338. throw new IllegalArgumentException("bandOffsets == null!");
  339. }
  340. int numBands = colorSpace.getNumComponents() +
  341. (hasAlpha ? 1 : 0);
  342. if (bandOffsets.length != numBands) {
  343. throw new IllegalArgumentException
  344. ("bandOffsets.length is wrong!");
  345. }
  346. if (dataType != DataBuffer.TYPE_BYTE &&
  347. dataType != DataBuffer.TYPE_SHORT &&
  348. dataType != DataBuffer.TYPE_USHORT &&
  349. dataType != DataBuffer.TYPE_INT &&
  350. dataType != DataBuffer.TYPE_FLOAT &&
  351. dataType != DataBuffer.TYPE_DOUBLE) {
  352. throw new IllegalArgumentException
  353. ("Bad value for dataType!");
  354. }
  355. this.colorSpace = colorSpace;
  356. this.bandOffsets = (int[])bandOffsets.clone();
  357. this.dataType = dataType;
  358. this.hasAlpha = hasAlpha;
  359. this.isAlphaPremultiplied = isAlphaPremultiplied;
  360. this.colorModel =
  361. ImageTypeSpecifier.createComponentCM(colorSpace,
  362. bandOffsets.length,
  363. dataType,
  364. hasAlpha,
  365. isAlphaPremultiplied);
  366. int minBandOffset = bandOffsets[0];
  367. int maxBandOffset = minBandOffset;
  368. for (int i = 0; i < bandOffsets.length; i++) {
  369. int offset = bandOffsets[i];
  370. minBandOffset = Math.min(offset, minBandOffset);
  371. maxBandOffset = Math.max(offset, maxBandOffset);
  372. }
  373. int pixelStride = maxBandOffset - minBandOffset + 1;
  374. int w = 1;
  375. int h = 1;
  376. this.sampleModel =
  377. new PixelInterleavedSampleModel(dataType,
  378. w, h,
  379. pixelStride,
  380. w*pixelStride,
  381. bandOffsets);
  382. }
  383. public boolean equals(Object o) {
  384. if ((o == null) ||
  385. !(o instanceof ImageTypeSpecifier.Interleaved)) {
  386. return false;
  387. }
  388. ImageTypeSpecifier.Interleaved that =
  389. (ImageTypeSpecifier.Interleaved)o;
  390. if ((!(this.colorSpace.equals(that.colorSpace))) ||
  391. (this.dataType != that.dataType) ||
  392. (this.hasAlpha != that.hasAlpha) ||
  393. (this.isAlphaPremultiplied != that.isAlphaPremultiplied) ||
  394. (this.bandOffsets.length != that.bandOffsets.length)) {
  395. return false;
  396. }
  397. for (int i = 0; i < bandOffsets.length; i++) {
  398. if (this.bandOffsets[i] != that.bandOffsets[i]) {
  399. return false;
  400. }
  401. }
  402. return true;
  403. }
  404. public int hashCode() {
  405. return (super.hashCode() +
  406. (4 * bandOffsets.length) +
  407. (25 * dataType) +
  408. (hasAlpha ? 17 : 18));
  409. }
  410. }
  411. /**
  412. * Returns a specifier for an interleaved image format that will
  413. * use a <code>ComponentColorModel</code> and a
  414. * <code>PixelInterleavedSampleModel</code> to store each pixel
  415. * component in a separate byte, short, or int.
  416. *
  417. * @param colorSpace the desired <code>ColorSpace</code>.
  418. * @param bandOffsets an array of <code>int</code>s indicating the
  419. * offsets for each band.
  420. * @param dataType the desired data type, as one of the enumerations
  421. * from the <code>DataBuffer</code> class.
  422. * @param hasAlpha <code>true</code> if an alpha channel is desired.
  423. * @param isAlphaPremultiplied <code>true</code> if the color channels
  424. * will be premultipled by the alpha channel.
  425. *
  426. * @return an <code>ImageTypeSpecifier</code> with the desired
  427. * characteristics.
  428. *
  429. * @exception IllegalArgumentException if <code>colorSpace</code>
  430. * is <code>null</code>.
  431. * @exception IllegalArgumentException if <code>bandOffsets</code>
  432. * is <code>null</code>.
  433. * @exception IllegalArgumentException if <code>dataType</code> is
  434. * not one of the legal <code>DataBuffer.TYPE_*</code> constants.
  435. * @exception IllegalArgumentException if
  436. * <code>bandOffsets.length</code> does not equal the number of
  437. * color space components, plus 1 if <code>hasAlpha</code> is
  438. * <code>true</code>.
  439. */
  440. public static ImageTypeSpecifier
  441. createInterleaved(ColorSpace colorSpace,
  442. int[] bandOffsets,
  443. int dataType,
  444. boolean hasAlpha,
  445. boolean isAlphaPremultiplied) {
  446. return new ImageTypeSpecifier.Interleaved(colorSpace,
  447. bandOffsets,
  448. dataType,
  449. hasAlpha,
  450. isAlphaPremultiplied);
  451. }
  452. // Banded
  453. static class Banded extends ImageTypeSpecifier {
  454. ColorSpace colorSpace;
  455. int[] bankIndices;
  456. int[] bandOffsets;
  457. int dataType;
  458. boolean hasAlpha;
  459. boolean isAlphaPremultiplied;
  460. public Banded(ColorSpace colorSpace,
  461. int[] bankIndices,
  462. int[] bandOffsets,
  463. int dataType,
  464. boolean hasAlpha,
  465. boolean isAlphaPremultiplied) {
  466. if (colorSpace == null) {
  467. throw new IllegalArgumentException("colorSpace == null!");
  468. }
  469. if (bankIndices == null) {
  470. throw new IllegalArgumentException("bankIndices == null!");
  471. }
  472. if (bandOffsets == null) {
  473. throw new IllegalArgumentException("bandOffsets == null!");
  474. }
  475. if (bankIndices.length != bandOffsets.length) {
  476. throw new IllegalArgumentException
  477. ("bankIndices.length != bandOffsets.length!");
  478. }
  479. if (dataType != DataBuffer.TYPE_BYTE &&
  480. dataType != DataBuffer.TYPE_SHORT &&
  481. dataType != DataBuffer.TYPE_USHORT &&
  482. dataType != DataBuffer.TYPE_INT &&
  483. dataType != DataBuffer.TYPE_FLOAT &&
  484. dataType != DataBuffer.TYPE_DOUBLE) {
  485. throw new IllegalArgumentException
  486. ("Bad value for dataType!");
  487. }
  488. int numBands = colorSpace.getNumComponents() +
  489. (hasAlpha ? 1 : 0);
  490. if (bandOffsets.length != numBands) {
  491. throw new IllegalArgumentException
  492. ("bandOffsets.length is wrong!");
  493. }
  494. this.colorSpace = colorSpace;
  495. this.bankIndices = (int[])bankIndices.clone();
  496. this.bandOffsets = (int[])bandOffsets.clone();
  497. this.dataType = dataType;
  498. this.hasAlpha = hasAlpha;
  499. this.isAlphaPremultiplied = isAlphaPremultiplied;
  500. this.colorModel =
  501. ImageTypeSpecifier.createComponentCM(colorSpace,
  502. bankIndices.length,
  503. dataType,
  504. hasAlpha,
  505. isAlphaPremultiplied);
  506. int w = 1;
  507. int h = 1;
  508. this.sampleModel = new BandedSampleModel(dataType,
  509. w, h,
  510. w,
  511. bankIndices,
  512. bandOffsets);
  513. }
  514. public boolean equals(Object o) {
  515. if ((o == null) ||
  516. !(o instanceof ImageTypeSpecifier.Banded)) {
  517. return false;
  518. }
  519. ImageTypeSpecifier.Banded that =
  520. (ImageTypeSpecifier.Banded)o;
  521. if ((!(this.colorSpace.equals(that.colorSpace))) ||
  522. (this.dataType != that.dataType) ||
  523. (this.hasAlpha != that.hasAlpha) ||
  524. (this.isAlphaPremultiplied != that.isAlphaPremultiplied) ||
  525. (this.bankIndices.length != that.bankIndices.length) ||
  526. (this.bandOffsets.length != that.bandOffsets.length)) {
  527. return false;
  528. }
  529. for (int i = 0; i < bankIndices.length; i++) {
  530. if (this.bankIndices[i] != that.bankIndices[i]) {
  531. return false;
  532. }
  533. }
  534. for (int i = 0; i < bandOffsets.length; i++) {
  535. if (this.bandOffsets[i] != that.bandOffsets[i]) {
  536. return false;
  537. }
  538. }
  539. return true;
  540. }
  541. public int hashCode() {
  542. return (super.hashCode() +
  543. (3 * bandOffsets.length) +
  544. (7 * bankIndices.length) +
  545. (21 * dataType) +
  546. (hasAlpha ? 19 : 29));
  547. }
  548. }
  549. /**
  550. * Returns a specifier for a banded image format that will use a
  551. * <code>ComponentColorModel</code> and a
  552. * <code>BandedSampleModel</code> to store each channel in a
  553. * separate array.
  554. *
  555. * @param colorSpace the desired <code>ColorSpace</code>.
  556. * @param bankIndices an array of <code>int</code>s indicating the
  557. * bank in which each band will be stored.
  558. * @param bandOffsets an array of <code>int</code>s indicating the
  559. * starting offset of each band within its bank.
  560. * @param dataType the desired data type, as one of the enumerations
  561. * from the <code>DataBuffer</code> class.
  562. * @param hasAlpha <code>true</code> if an alpha channel is desired.
  563. * @param isAlphaPremultiplied <code>true</code> if the color channels
  564. * will be premultipled by the alpha channel.
  565. *
  566. * @return an <code>ImageTypeSpecifier</code> with the desired
  567. * characteristics.
  568. *
  569. * @exception IllegalArgumentException if <code>colorSpace</code>
  570. * is <code>null</code>.
  571. * @exception IllegalArgumentException if <code>bankIndices</code>
  572. * is <code>null</code>.
  573. * @exception IllegalArgumentException if <code>bandOffsets</code>
  574. * is <code>null</code>.
  575. * @exception IllegalArgumentException if the lengths of
  576. * <code>bankIndices</code> and <code>bandOffsets</code> differ.
  577. * @exception IllegalArgumentException if
  578. * <code>bandOffsets.length</code> does not equal the number of
  579. * color space components, plus 1 if <code>hasAlpha</code> is
  580. * <code>true</code>.
  581. * @exception IllegalArgumentException if <code>dataType</code> is
  582. * not one of the legal <code>DataBuffer.TYPE_*</code> constants.
  583. */
  584. public static ImageTypeSpecifier
  585. createBanded(ColorSpace colorSpace,
  586. int[] bankIndices,
  587. int[] bandOffsets,
  588. int dataType,
  589. boolean hasAlpha,
  590. boolean isAlphaPremultiplied) {
  591. return new ImageTypeSpecifier.Banded(colorSpace,
  592. bankIndices,
  593. bandOffsets,
  594. dataType,
  595. hasAlpha,
  596. isAlphaPremultiplied);
  597. }
  598. // Grayscale
  599. static class Grayscale extends ImageTypeSpecifier {
  600. int bits;
  601. int dataType;
  602. boolean isSigned;
  603. boolean hasAlpha;
  604. boolean isAlphaPremultiplied;
  605. public Grayscale(int bits,
  606. int dataType,
  607. boolean isSigned,
  608. boolean hasAlpha,
  609. boolean isAlphaPremultiplied)
  610. {
  611. if (bits != 1 && bits != 2 && bits != 4 &&
  612. bits != 8 && bits != 16)
  613. {
  614. throw new IllegalArgumentException("Bad value for bits!");
  615. }
  616. if (dataType != DataBuffer.TYPE_BYTE &&
  617. dataType != DataBuffer.TYPE_SHORT &&
  618. dataType != DataBuffer.TYPE_USHORT)
  619. {
  620. throw new IllegalArgumentException
  621. ("Bad value for dataType!");
  622. }
  623. if (bits > 8 && dataType == DataBuffer.TYPE_BYTE) {
  624. throw new IllegalArgumentException
  625. ("Too many bits for dataType!");
  626. }
  627. this.bits = bits;
  628. this.dataType = dataType;
  629. this.isSigned = isSigned;
  630. this.hasAlpha = hasAlpha;
  631. this.isAlphaPremultiplied = isAlphaPremultiplied;
  632. ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
  633. if ((bits == 8 && dataType == DataBuffer.TYPE_BYTE) ||
  634. (bits == 16 &&
  635. (dataType == DataBuffer.TYPE_SHORT ||
  636. dataType == DataBuffer.TYPE_USHORT))) {
  637. // Use component color model & sample model
  638. int numBands = hasAlpha ? 2 : 1;
  639. int transparency =
  640. hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE;
  641. int[] nBits = new int[numBands];
  642. nBits[0] = bits;
  643. if (numBands == 2) {
  644. nBits[1] = bits;
  645. }
  646. this.colorModel =
  647. new ComponentColorModel(colorSpace,
  648. nBits,
  649. hasAlpha,
  650. isAlphaPremultiplied,
  651. transparency,
  652. dataType);
  653. int[] bandOffsets = new int[numBands];
  654. bandOffsets[0] = 0;
  655. if (numBands == 2) {
  656. bandOffsets[1] = 1;
  657. }
  658. int w = 1;
  659. int h = 1;
  660. this.sampleModel =
  661. new PixelInterleavedSampleModel(dataType,
  662. w, h,
  663. numBands, w*numBands,
  664. bandOffsets);
  665. } else {
  666. int numEntries = 1 << bits;
  667. byte[] arr = new byte[numEntries];
  668. for (int i = 0; i < numEntries; i++) {
  669. arr[i] = (byte)(i*255/(numEntries - 1));
  670. }
  671. this.colorModel =
  672. new IndexColorModel(bits, numEntries, arr, arr, arr);
  673. this.sampleModel =
  674. new MultiPixelPackedSampleModel(dataType, 1, 1, bits);
  675. }
  676. }
  677. }
  678. /**
  679. * Returns a specifier for a grayscale image format that will pack
  680. * pixels of the given bit depth into array elements of
  681. * the specified data type.
  682. *
  683. * @param bits the number of bits per gray value (1, 2, 4, 8, or 16).
  684. * @param dataType the desired data type, as one of the enumerations
  685. * from the <code>DataBuffer</code> class.
  686. * @param isSigned <code>true</code> if negative values are to
  687. * be represented.
  688. *
  689. * @return an <code>ImageTypeSpecifier</code> with the desired
  690. * characteristics.
  691. *
  692. * @exception IllegalArgumentException if <code>bits</code> is
  693. * not one of 1, 2, 4, 8, or 16.
  694. * @exception IllegalArgumentException if <code>dataType</code> is
  695. * not one of <code>DataBuffer.TYPE_BYTE</code>,
  696. * <code>DataBuffer.TYPE_SHORT</code>, or
  697. * <code>DataBuffer.TYPE_USHORT</code>.
  698. * @exception IllegalArgumentException if <code>bits</code> is
  699. * larger than the bit size of the given <code>dataType</code>.
  700. */
  701. public static ImageTypeSpecifier
  702. createGrayscale(int bits,
  703. int dataType,
  704. boolean isSigned) {
  705. return new ImageTypeSpecifier.Grayscale(bits,
  706. dataType,
  707. isSigned,
  708. false,
  709. false);
  710. }
  711. /**
  712. * Returns a specifier for a grayscale plus alpha image format
  713. * that will pack pixels of the given bit depth into array
  714. * elements of the specified data type.
  715. *
  716. * @param bits the number of bits per gray value (1, 2, 4, 8, or 16).
  717. * @param dataType the desired data type, as one of the enumerations
  718. * from the <code>DataBuffer</code> class.
  719. * @param isSigned <code>true</code> if negative values are to
  720. * be represented.
  721. * @param isAlphaPremultiplied <code>true</code> if the luminance channel
  722. * will be premultipled by the alpha channel.
  723. *
  724. * @return an <code>ImageTypeSpecifier</code> with the desired
  725. * characteristics.
  726. *
  727. * @exception IllegalArgumentException if <code>bits</code> is
  728. * not one of 1, 2, 4, 8, or 16.
  729. * @exception IllegalArgumentException if <code>dataType</code> is
  730. * not one of <code>DataBuffer.TYPE_BYTE</code>,
  731. * <code>DataBuffer.TYPE_SHORT</code>, or
  732. * <code>DataBuffer.TYPE_USHORT</code>.
  733. * @exception IllegalArgumentException if <code>bits</code> is
  734. * larger than the bit size of the given <code>dataType</code>.
  735. */
  736. public static ImageTypeSpecifier
  737. createGrayscale(int bits,
  738. int dataType,
  739. boolean isSigned,
  740. boolean isAlphaPremultiplied) {
  741. return new ImageTypeSpecifier.Grayscale(bits,
  742. dataType,
  743. isSigned,
  744. true,
  745. isAlphaPremultiplied);
  746. }
  747. // Indexed
  748. static class Indexed extends ImageTypeSpecifier {
  749. byte[] redLUT;
  750. byte[] greenLUT;
  751. byte[] blueLUT;
  752. byte[] alphaLUT = null;
  753. int bits;
  754. int dataType;
  755. public Indexed(byte[] redLUT,
  756. byte[] greenLUT,
  757. byte[] blueLUT,
  758. byte[] alphaLUT,
  759. int bits,
  760. int dataType) {
  761. if (redLUT == null || greenLUT == null || blueLUT == null) {
  762. throw new IllegalArgumentException("LUT is null!");
  763. }
  764. if (bits != 1 && bits != 2 && bits != 4 &&
  765. bits != 8 && bits != 16) {
  766. throw new IllegalArgumentException("Bad value for bits!");
  767. }
  768. if (dataType != DataBuffer.TYPE_BYTE &&
  769. dataType != DataBuffer.TYPE_SHORT &&
  770. dataType != DataBuffer.TYPE_USHORT &&
  771. dataType != DataBuffer.TYPE_INT) {
  772. throw new IllegalArgumentException
  773. ("Bad value for dataType!");
  774. }
  775. if ((bits > 8 && dataType == DataBuffer.TYPE_BYTE) ||
  776. (bits > 16 && dataType != DataBuffer.TYPE_INT)) {
  777. throw new IllegalArgumentException
  778. ("Too many bits for dataType!");
  779. }
  780. int len = 1 << bits;
  781. if (redLUT.length != len ||
  782. greenLUT.length != len ||
  783. blueLUT.length != len ||
  784. (alphaLUT != null && alphaLUT.length != len)) {
  785. throw new IllegalArgumentException("LUT has improper length!");
  786. }
  787. this.redLUT = (byte[])redLUT.clone();
  788. this.greenLUT = (byte[])greenLUT.clone();
  789. this.blueLUT = (byte[])blueLUT.clone();
  790. if (alphaLUT != null) {
  791. this.alphaLUT = (byte[])alphaLUT.clone();
  792. }
  793. this.bits = bits;
  794. this.dataType = dataType;
  795. if (alphaLUT == null) {
  796. this.colorModel = new IndexColorModel(bits,
  797. redLUT.length,
  798. redLUT,
  799. greenLUT,
  800. blueLUT);
  801. } else {
  802. this.colorModel = new IndexColorModel(bits,
  803. redLUT.length,
  804. redLUT,
  805. greenLUT,
  806. blueLUT,
  807. alphaLUT);
  808. }
  809. if ((bits == 8 && dataType == DataBuffer.TYPE_BYTE) ||
  810. (bits == 16 &&
  811. (dataType == DataBuffer.TYPE_SHORT ||
  812. dataType == DataBuffer.TYPE_USHORT))) {
  813. int[] bandOffsets = { 0 };
  814. this.sampleModel =
  815. new PixelInterleavedSampleModel(dataType,
  816. 1, 1, 1, 1,
  817. bandOffsets);
  818. } else {
  819. this.sampleModel =
  820. new MultiPixelPackedSampleModel(dataType, 1, 1, bits);
  821. }
  822. }
  823. }
  824. /**
  825. * Returns a specifier for an indexed-color image format that will pack
  826. * index values of the given bit depth into array elements of
  827. * the specified data type.
  828. *
  829. * @param redLUT an array of <code>byte</code>s containing
  830. * the red values for each index.
  831. * @param greenLUT an array of <code>byte</code>s containing * the
  832. * green values for each index.
  833. * @param blueLUT an array of <code>byte</code>s containing the
  834. * blue values for each index.
  835. * @param alphaLUT an array of <code>byte</code>s containing the
  836. * alpha values for each index, or <code>null</code> to create a
  837. * fully opaque LUT.
  838. * @param bits the number of bits in each index.
  839. * @param dataType the desired output type, as one of the enumerations
  840. * from the <code>DataBuffer</code> class.
  841. *
  842. * @return an <code>ImageTypeSpecifier</code> with the desired
  843. * characteristics.
  844. *
  845. * @exception IllegalArgumentException if <code>redLUT</code> is
  846. * <code>null</code>.
  847. * @exception IllegalArgumentException if <code>greenLUT</code> is
  848. * <code>null</code>.
  849. * @exception IllegalArgumentException if <code>blueLUT</code> is
  850. * <code>null</code>.
  851. * @exception IllegalArgumentException if <code>bits</code> is
  852. * not one of 1, 2, 4, 8, or 16.
  853. * @exception IllegalArgumentException if the
  854. * non-<code>null</code> LUT parameters do not have lengths of
  855. * exactly <code>1 << bits</code>.
  856. * @exception IllegalArgumentException if <code>dataType</code> is
  857. * not one of <code>DataBuffer.TYPE_BYTE</code>,
  858. * <code>DataBuffer.TYPE_SHORT</code>,
  859. * <code>DataBuffer.TYPE_USHORT</code>,
  860. * or <code>DataBuffer.TYPE_INT</code>.
  861. * @exception IllegalArgumentException if <code>bits</code> is
  862. * larger than the bit size of the given <code>dataType</code>.
  863. */
  864. public static ImageTypeSpecifier
  865. createIndexed(byte[] redLUT,
  866. byte[] greenLUT,
  867. byte[] blueLUT,
  868. byte[] alphaLUT,
  869. int bits,
  870. int dataType) {
  871. return new ImageTypeSpecifier.Indexed(redLUT,
  872. greenLUT,
  873. blueLUT,
  874. alphaLUT,
  875. bits,
  876. dataType);
  877. }
  878. /**
  879. * Returns an <code>ImageTypeSpecifier</code> that encodes
  880. * one of the standard <code>BufferedImage</code> types
  881. * (other than <code>TYPE_CUSTOM</code>).
  882. *
  883. * @param bufferedImageType an int representing one of the standard
  884. * <code>BufferedImage</code> types.
  885. *
  886. * @return an <code>ImageTypeSpecifier</code> with the desired
  887. * characteristics.
  888. *
  889. * @exception IllegalArgumentException if
  890. * <code>bufferedImageType</code> is not one of the standard
  891. * types, or is equal to <code>TYPE_CUSTOM</code>.
  892. *
  893. * @see java.awt.image.BufferedImage
  894. * @see java.awt.image.BufferedImage#TYPE_INT_RGB
  895. * @see java.awt.image.BufferedImage#TYPE_INT_ARGB
  896. * @see java.awt.image.BufferedImage#TYPE_INT_ARGB_PRE
  897. * @see java.awt.image.BufferedImage#TYPE_INT_BGR
  898. * @see java.awt.image.BufferedImage#TYPE_3BYTE_BGR
  899. * @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR
  900. * @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR_PRE
  901. * @see java.awt.image.BufferedImage#TYPE_USHORT_565_RGB
  902. * @see java.awt.image.BufferedImage#TYPE_USHORT_555_RGB
  903. * @see java.awt.image.BufferedImage#TYPE_BYTE_GRAY
  904. * @see java.awt.image.BufferedImage#TYPE_USHORT_GRAY
  905. * @see java.awt.image.BufferedImage#TYPE_BYTE_BINARY
  906. * @see java.awt.image.BufferedImage#TYPE_BYTE_INDEXED
  907. */
  908. public static
  909. ImageTypeSpecifier createFromBufferedImageType(int bufferedImageType) {
  910. if (bufferedImageType >= BufferedImage.TYPE_INT_RGB &&
  911. bufferedImageType <= BufferedImage.TYPE_BYTE_INDEXED) {
  912. return BISpecifier[bufferedImageType];
  913. } else if (bufferedImageType == BufferedImage.TYPE_CUSTOM) {
  914. throw new IllegalArgumentException("Cannot create from TYPE_CUSTOM!");
  915. } else {
  916. throw new IllegalArgumentException("Invalid BufferedImage type!");
  917. }
  918. }
  919. /**
  920. * Returns an <code>ImageTypeSpecifier</code> that encodes the
  921. * layout of a <code>RenderedImage</code> (which may be a
  922. * <code>BufferedImage</code>).
  923. *
  924. * @param image a <code>RenderedImage</code>.
  925. *
  926. * @return an <code>ImageTypeSpecifier</code> with the desired
  927. * characteristics.
  928. *
  929. * @exception IllegalArgumentException if <code>image</code> is
  930. * <code>null</code>.
  931. */
  932. public static
  933. ImageTypeSpecifier createFromRenderedImage(RenderedImage image) {
  934. if (image == null) {
  935. throw new IllegalArgumentException("image == null!");
  936. }
  937. if (image instanceof BufferedImage) {
  938. int bufferedImageType = ((BufferedImage)image).getType();
  939. if (bufferedImageType != BufferedImage.TYPE_CUSTOM) {
  940. return BISpecifier[bufferedImageType];
  941. }
  942. }
  943. return new ImageTypeSpecifier(image);
  944. }
  945. /**
  946. * Returns an int containing one of the enumerated constant values
  947. * describing image formats from <code>BufferedImage</code>.
  948. *
  949. * @return an <code>int</code> representing a
  950. * <code>BufferedImage</code> type.
  951. *
  952. * @see java.awt.image.BufferedImage
  953. * @see java.awt.image.BufferedImage#TYPE_CUSTOM
  954. * @see java.awt.image.BufferedImage#TYPE_INT_RGB
  955. * @see java.awt.image.BufferedImage#TYPE_INT_ARGB
  956. * @see java.awt.image.BufferedImage#TYPE_INT_ARGB_PRE
  957. * @see java.awt.image.BufferedImage#TYPE_INT_BGR
  958. * @see java.awt.image.BufferedImage#TYPE_3BYTE_BGR
  959. * @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR
  960. * @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR_PRE
  961. * @see java.awt.image.BufferedImage#TYPE_USHORT_565_RGB
  962. * @see java.awt.image.BufferedImage#TYPE_USHORT_555_RGB
  963. * @see java.awt.image.BufferedImage#TYPE_BYTE_GRAY
  964. * @see java.awt.image.BufferedImage#TYPE_USHORT_GRAY
  965. * @see java.awt.image.BufferedImage#TYPE_BYTE_BINARY
  966. * @see java.awt.image.BufferedImage#TYPE_BYTE_INDEXED
  967. */
  968. public int getBufferedImageType() {
  969. BufferedImage bi = createBufferedImage(1, 1);
  970. return bi.getType();
  971. }
  972. /**
  973. * Return the number of color components
  974. * specified by this object. This is the same value as returned by
  975. * <code>ColorModel.getNumComponents</code>
  976. *
  977. * @return the number of components in the image.
  978. */
  979. public int getNumComponents() {
  980. return colorModel.getNumComponents();
  981. }
  982. /**
  983. * Return the number of bands
  984. * specified by this object. This is the same value as returned by
  985. * <code>SampleModel.getNumBands</code>
  986. *
  987. * @return the number of bands in the image.
  988. */
  989. public int getNumBands() {
  990. return sampleModel.getNumBands();
  991. }
  992. /**
  993. * Return the number of bits used to represent samples of the given band.
  994. *
  995. * @param band the index of the band to be queried, as an
  996. * int.
  997. *
  998. * @return an int specifying a number of bits.
  999. *
  1000. * @exception IllegalArgumentException if <code>band</code> is
  1001. * negative or greater than the largest band index.
  1002. */
  1003. public int getBitsPerBand(int band) {
  1004. if (band < 0 | band >= getNumBands()) {
  1005. throw new IllegalArgumentException("band out of range!");
  1006. }
  1007. return sampleModel.getSampleSize(band);
  1008. }
  1009. /**
  1010. * Returns a <code>SampleModel</code> based on the settings
  1011. * encapsulated within this object. The width and height of the
  1012. * <code>SampleModel</code> will be set to arbitrary values.
  1013. *
  1014. * @return a <code>SampleModel</code> with arbitrary dimensions.
  1015. */
  1016. public SampleModel getSampleModel() {
  1017. return sampleModel;
  1018. }
  1019. /**
  1020. * Returns a <code>SampleModel</code> based on the settings
  1021. * encapsulated within this object. The width and height of the
  1022. * <code>SampleModel</code> will be set to the supplied values.
  1023. *
  1024. * @param width the desired width of the returned <code>SampleModel</code>.
  1025. * @param height the desired height of the returned
  1026. * <code>SampleModel</code>.
  1027. *
  1028. * @return a <code>SampleModel</code> with the given dimensions.
  1029. *
  1030. * @exception IllegalArgumentException if either <code>width</code> or
  1031. * <code>height</code> are negative or zero.
  1032. * @exception IllegalArgumentException if the product of
  1033. * <code>width</code> and <code>height</code> is greater than
  1034. * <code>Integer.MAX_VALUE</code>
  1035. */
  1036. public SampleModel getSampleModel(int width, int height) {
  1037. if ((long)width*height > Integer.MAX_VALUE) {
  1038. throw new IllegalArgumentException
  1039. ("width*height > Integer.MAX_VALUE!");
  1040. }
  1041. return sampleModel.createCompatibleSampleModel(width, height);
  1042. }
  1043. /**
  1044. * Returns the <code>ColorModel</code> specified by this object.
  1045. *
  1046. * @return a <code>ColorModel</code>.
  1047. */
  1048. public ColorModel getColorModel() {
  1049. return colorModel;
  1050. }
  1051. /**
  1052. * Creates a <code>BufferedImage</code> with a given width and
  1053. * height according to the specification embodied in this object.
  1054. *
  1055. * @param width the desired width of the returned
  1056. * <code>BufferedImage</code>.
  1057. * @param height the desired height of the returned
  1058. * <code>BufferedImage</code>.
  1059. *
  1060. * @return a new <code>BufferedImage</code>
  1061. *
  1062. * @exception IllegalArgumentException if either <code>width</code> or
  1063. * <code>height</code> are negative or zero.
  1064. * @exception IllegalArgumentException if the product of
  1065. * <code>width</code> and <code>height</code> is greater than
  1066. * <code>Integer.MAX_VALUE</code>, or if the number of array
  1067. * elements needed to store the image is greater than
  1068. * <code>Integer.MAX_VALUE</code>.
  1069. */
  1070. public BufferedImage createBufferedImage(int width, int height) {
  1071. try {
  1072. SampleModel sampleModel = getSampleModel(width, height);
  1073. WritableRaster raster =
  1074. Raster.createWritableRaster(sampleModel,
  1075. new Point(0, 0));
  1076. return new BufferedImage(colorModel, raster,
  1077. colorModel.isAlphaPremultiplied(),
  1078. new Hashtable());
  1079. } catch (NegativeArraySizeException e) {
  1080. // Exception most likely thrown from a DataBuffer constructor
  1081. throw new IllegalArgumentException
  1082. ("Array size > Integer.MAX_VALUE!");
  1083. }
  1084. }
  1085. /**
  1086. * Returns <code>true</code> if the given <code>Object</code> is
  1087. * an <code>ImageTypeSpecifier</code> and has a
  1088. * <code>SampleModel</code> and <code>ColorModel</code> that are
  1089. * equal to those of this object.
  1090. *
  1091. * @param o the <code>Object</code> to be compared for equality.
  1092. *
  1093. * @return <code>true</code> if the given object is an equivalent
  1094. * <code>ImageTypeSpecifier</code>.
  1095. */
  1096. public boolean equals(Object o) {
  1097. if ((o == null) || !(o instanceof ImageTypeSpecifier)) {
  1098. return false;
  1099. }
  1100. ImageTypeSpecifier that = (ImageTypeSpecifier)o;
  1101. return (colorModel.equals(that.colorModel)) &&
  1102. (sampleModel.equals(that.sampleModel));
  1103. }
  1104. /**
  1105. * Returns the hash code for this ImageTypeSpecifier.
  1106. *
  1107. * @return a hash code for this ImageTypeSpecifier
  1108. */
  1109. public int hashCode() {
  1110. return (9 * colorModel.hashCode()) + (14 * sampleModel.hashCode());
  1111. }
  1112. }