1. /*
  2. * @(#)ImageTypeSpecifier.java 1.32 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 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. }
  405. /**
  406. * Returns a specifier for an interleaved image format that will
  407. * use a <code>ComponentColorModel</code> and a
  408. * <code>PixelInterleavedSampleModel</code> to store each pixel
  409. * component in a separate byte, short, or int.
  410. *
  411. * @param colorSpace the desired <code>ColorSpace</code>.
  412. * @param bandOffsets an array of <code>int</code>s indicating the
  413. * offsets for each band.
  414. * @param dataType the desired data type, as one of the enumerations
  415. * from the <code>DataBuffer</code> class.
  416. * @param hasAlpha <code>true</code> if an alpha channel is desired.
  417. * @param isAlphaPremultiplied <code>true</code> if the color channels
  418. * will be premultipled by the alpha channel.
  419. *
  420. * @return an <code>ImageTypeSpecifier</code> with the desired
  421. * characteristics.
  422. *
  423. * @exception IllegalArgumentException if <code>colorSpace</code>
  424. * is <code>null</code>.
  425. * @exception IllegalArgumentException if <code>bandOffsets</code>
  426. * is <code>null</code>.
  427. * @exception IllegalArgumentException if <code>dataType</code> is
  428. * not one of the legal <code>DataBuffer.TYPE_*</code> constants.
  429. * @exception IllegalArgumentException if
  430. * <code>bandOffsets.length</code> does not equal the number of
  431. * color space components, plus 1 if <code>hasAlpha</code> is
  432. * <code>true</code>.
  433. */
  434. public static ImageTypeSpecifier
  435. createInterleaved(ColorSpace colorSpace,
  436. int[] bandOffsets,
  437. int dataType,
  438. boolean hasAlpha,
  439. boolean isAlphaPremultiplied) {
  440. return new ImageTypeSpecifier.Interleaved(colorSpace,
  441. bandOffsets,
  442. dataType,
  443. hasAlpha,
  444. isAlphaPremultiplied);
  445. }
  446. // Banded
  447. static class Banded extends ImageTypeSpecifier {
  448. ColorSpace colorSpace;
  449. int[] bankIndices;
  450. int[] bandOffsets;
  451. int dataType;
  452. boolean hasAlpha;
  453. boolean isAlphaPremultiplied;
  454. public Banded(ColorSpace colorSpace,
  455. int[] bankIndices,
  456. int[] bandOffsets,
  457. int dataType,
  458. boolean hasAlpha,
  459. boolean isAlphaPremultiplied) {
  460. if (colorSpace == null) {
  461. throw new IllegalArgumentException("colorSpace == null!");
  462. }
  463. if (bankIndices == null) {
  464. throw new IllegalArgumentException("bankIndices == null!");
  465. }
  466. if (bandOffsets == null) {
  467. throw new IllegalArgumentException("bandOffsets == null!");
  468. }
  469. if (bankIndices.length != bandOffsets.length) {
  470. throw new IllegalArgumentException
  471. ("bankIndices.length != bandOffsets.length!");
  472. }
  473. if (dataType != DataBuffer.TYPE_BYTE &&
  474. dataType != DataBuffer.TYPE_SHORT &&
  475. dataType != DataBuffer.TYPE_USHORT &&
  476. dataType != DataBuffer.TYPE_INT &&
  477. dataType != DataBuffer.TYPE_FLOAT &&
  478. dataType != DataBuffer.TYPE_DOUBLE) {
  479. throw new IllegalArgumentException
  480. ("Bad value for dataType!");
  481. }
  482. int numBands = colorSpace.getNumComponents() +
  483. (hasAlpha ? 1 : 0);
  484. if (bandOffsets.length != numBands) {
  485. throw new IllegalArgumentException
  486. ("bandOffsets.length is wrong!");
  487. }
  488. this.colorSpace = colorSpace;
  489. this.bankIndices = (int[])bankIndices.clone();
  490. this.bandOffsets = (int[])bandOffsets.clone();
  491. this.dataType = dataType;
  492. this.hasAlpha = hasAlpha;
  493. this.isAlphaPremultiplied = isAlphaPremultiplied;
  494. this.colorModel =
  495. ImageTypeSpecifier.createComponentCM(colorSpace,
  496. bankIndices.length,
  497. dataType,
  498. hasAlpha,
  499. isAlphaPremultiplied);
  500. int w = 1;
  501. int h = 1;
  502. this.sampleModel = new BandedSampleModel(dataType,
  503. w, h,
  504. w,
  505. bankIndices,
  506. bandOffsets);
  507. }
  508. public boolean equals(Object o) {
  509. if ((o == null) ||
  510. !(o instanceof ImageTypeSpecifier.Banded)) {
  511. return false;
  512. }
  513. ImageTypeSpecifier.Banded that =
  514. (ImageTypeSpecifier.Banded)o;
  515. if ((!(this.colorSpace.equals(that.colorSpace))) ||
  516. (this.dataType != that.dataType) ||
  517. (this.hasAlpha != that.hasAlpha) ||
  518. (this.isAlphaPremultiplied != that.isAlphaPremultiplied) ||
  519. (this.bankIndices.length != that.bankIndices.length) ||
  520. (this.bandOffsets.length != that.bandOffsets.length)) {
  521. return false;
  522. }
  523. for (int i = 0; i < bankIndices.length; i++) {
  524. if (this.bankIndices[i] != that.bankIndices[i]) {
  525. return false;
  526. }
  527. }
  528. for (int i = 0; i < bandOffsets.length; i++) {
  529. if (this.bandOffsets[i] != that.bandOffsets[i]) {
  530. return false;
  531. }
  532. }
  533. return true;
  534. }
  535. }
  536. /**
  537. * Returns a specifier for a banded image format that will use a
  538. * <code>ComponentColorModel</code> and a
  539. * <code>BandedSampleModel</code> to store each channel in a
  540. * separate array.
  541. *
  542. * @param colorSpace the desired <code>ColorSpace</code>.
  543. * @param bankIndices an array of <code>int</code>s indicating the
  544. * bank in which each band will be stored.
  545. * @param bandOffsets an array of <code>int</code>s indicating the
  546. * starting offset of each band within its bank.
  547. * @param dataType the desired data type, as one of the enumerations
  548. * from the <code>DataBuffer</code> class.
  549. * @param hasAlpha <code>true</code> if an alpha channel is desired.
  550. * @param isAlphaPremultiplied <code>true</code> if the color channels
  551. * will be premultipled by the alpha channel.
  552. *
  553. * @return an <code>ImageTypeSpecifier</code> with the desired
  554. * characteristics.
  555. *
  556. * @exception IllegalArgumentException if <code>colorSpace</code>
  557. * is <code>null</code>.
  558. * @exception IllegalArgumentException if <code>bankIndices</code>
  559. * is <code>null</code>.
  560. * @exception IllegalArgumentException if <code>bandOffsets</code>
  561. * is <code>null</code>.
  562. * @exception IllegalArgumentException if the lengths of
  563. * <code>bankIndices</code> and <code>bandOffsets</code> differ.
  564. * @exception IllegalArgumentException if
  565. * <code>bandOffsets.length</code> does not equal the number of
  566. * color space components, plus 1 if <code>hasAlpha</code> is
  567. * <code>true</code>.
  568. * @exception IllegalArgumentException if <code>dataType</code> is
  569. * not one of the legal <code>DataBuffer.TYPE_*</code> constants.
  570. */
  571. public static ImageTypeSpecifier
  572. createBanded(ColorSpace colorSpace,
  573. int[] bankIndices,
  574. int[] bandOffsets,
  575. int dataType,
  576. boolean hasAlpha,
  577. boolean isAlphaPremultiplied) {
  578. return new ImageTypeSpecifier.Banded(colorSpace,
  579. bankIndices,
  580. bandOffsets,
  581. dataType,
  582. hasAlpha,
  583. isAlphaPremultiplied);
  584. }
  585. // Grayscale
  586. static class Grayscale extends ImageTypeSpecifier {
  587. int bits;
  588. int dataType;
  589. boolean isSigned;
  590. boolean hasAlpha;
  591. boolean isAlphaPremultiplied;
  592. public Grayscale(int bits,
  593. int dataType,
  594. boolean isSigned,
  595. boolean hasAlpha,
  596. boolean isAlphaPremultiplied) {
  597. this.bits = bits;
  598. this.dataType = dataType;
  599. this.isSigned = isSigned;
  600. this.hasAlpha = hasAlpha;
  601. this.isAlphaPremultiplied = isAlphaPremultiplied;
  602. ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
  603. if ((bits == 8 && dataType == DataBuffer.TYPE_BYTE) ||
  604. (bits == 16 &&
  605. (dataType == DataBuffer.TYPE_SHORT ||
  606. dataType == DataBuffer.TYPE_USHORT))) {
  607. // Use component color model & sample model
  608. int numBands = hasAlpha ? 2 : 1;
  609. int transparency =
  610. hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE;
  611. int[] nBits = new int[numBands];
  612. nBits[0] = bits;
  613. if (numBands == 2) {
  614. nBits[1] = bits;
  615. }
  616. this.colorModel =
  617. new ComponentColorModel(colorSpace,
  618. nBits,
  619. hasAlpha,
  620. isAlphaPremultiplied,
  621. transparency,
  622. dataType);
  623. int[] bandOffsets = new int[numBands];
  624. bandOffsets[0] = 0;
  625. if (numBands == 2) {
  626. bandOffsets[1] = 1;
  627. }
  628. int w = 1;
  629. int h = 1;
  630. this.sampleModel =
  631. new PixelInterleavedSampleModel(dataType,
  632. w, h,
  633. numBands, w*numBands,
  634. bandOffsets);
  635. } else {
  636. int numEntries = 1 << bits;
  637. byte[] arr = new byte[numEntries];
  638. for (int i = 0; i < numEntries; i++) {
  639. arr[i] = (byte)(i*255/(numEntries - 1));
  640. }
  641. this.colorModel =
  642. new IndexColorModel(bits, numEntries, arr, arr, arr);
  643. this.sampleModel =
  644. new MultiPixelPackedSampleModel(dataType, 1, 1, bits);
  645. }
  646. }
  647. }
  648. /**
  649. * Returns a specifier for a grayscale image format that will pack
  650. * pixels of the given bit depth into array elements of
  651. * the specified data type.
  652. *
  653. * @param bits the number of bits per gray value (1, 2, 4, 8, or 16).
  654. * @param dataType the desired data type, as one of the enumerations
  655. * from the <code>DataBuffer</code> class.
  656. * @param isSigned <code>true</code> if negative values are to
  657. * be represented.
  658. *
  659. * @return an <code>ImageTypeSpecifier</code> with the desired
  660. * characteristics.
  661. */
  662. public static ImageTypeSpecifier
  663. createGrayscale(int bits,
  664. int dataType,
  665. boolean isSigned) {
  666. return new ImageTypeSpecifier.Grayscale(bits,
  667. dataType,
  668. isSigned,
  669. false,
  670. false);
  671. }
  672. /**
  673. * Returns a specifier for a grayscale plus alpha image format
  674. * that will pack pixels of the given bit depth into array
  675. * elements of the specified data type.
  676. *
  677. * @param bits the number of bits per gray value (1, 2, 4, 8, or 16).
  678. * @param dataType the desired data type, as one of the enumerations
  679. * from the <code>DataBuffer</code> class.
  680. * @param isSigned <code>true</code> if negative values are to
  681. * be represented.
  682. * @param isAlphaPremultiplied <code>true</code> if the luminance channel
  683. * will be premultipled by the alpha channel.
  684. *
  685. * @return an <code>ImageTypeSpecifier</code> with the desired
  686. * characteristics.
  687. */
  688. public static ImageTypeSpecifier
  689. createGrayscale(int bits,
  690. int dataType,
  691. boolean isSigned,
  692. boolean isAlphaPremultiplied) {
  693. return new ImageTypeSpecifier.Grayscale(bits,
  694. dataType,
  695. isSigned,
  696. true,
  697. isAlphaPremultiplied);
  698. }
  699. // Indexed
  700. static class Indexed extends ImageTypeSpecifier {
  701. byte[] redLUT;
  702. byte[] greenLUT;
  703. byte[] blueLUT;
  704. byte[] alphaLUT = null;
  705. int bits;
  706. int dataType;
  707. public Indexed(byte[] redLUT,
  708. byte[] greenLUT,
  709. byte[] blueLUT,
  710. byte[] alphaLUT,
  711. int bits,
  712. int dataType) {
  713. if (redLUT == null || greenLUT == null || blueLUT == null) {
  714. throw new IllegalArgumentException("LUT is null!");
  715. }
  716. if (bits != 1 && bits != 2 && bits != 4 &&
  717. bits != 8 && bits != 16) {
  718. throw new IllegalArgumentException("Bad value for bits!");
  719. }
  720. if (dataType != DataBuffer.TYPE_BYTE &&
  721. dataType != DataBuffer.TYPE_SHORT &&
  722. dataType != DataBuffer.TYPE_USHORT &&
  723. dataType != DataBuffer.TYPE_INT) {
  724. throw new IllegalArgumentException
  725. ("Bad value for dataType!");
  726. }
  727. if ((bits > 8 && dataType == DataBuffer.TYPE_BYTE) ||
  728. (bits > 16 && dataType != DataBuffer.TYPE_INT)) {
  729. throw new IllegalArgumentException
  730. ("Too many bits for dataType!");
  731. }
  732. int len = 1 << bits;
  733. if (redLUT.length != len ||
  734. greenLUT.length != len ||
  735. blueLUT.length != len ||
  736. (alphaLUT != null && alphaLUT.length != len)) {
  737. throw new IllegalArgumentException("LUT has improper length!");
  738. }
  739. this.redLUT = (byte[])redLUT.clone();
  740. this.greenLUT = (byte[])greenLUT.clone();
  741. this.blueLUT = (byte[])blueLUT.clone();
  742. if (alphaLUT != null) {
  743. this.alphaLUT = (byte[])alphaLUT.clone();
  744. }
  745. this.bits = bits;
  746. this.dataType = dataType;
  747. if (alphaLUT == null) {
  748. this.colorModel = new IndexColorModel(bits,
  749. redLUT.length,
  750. redLUT,
  751. greenLUT,
  752. blueLUT);
  753. } else {
  754. this.colorModel = new IndexColorModel(bits,
  755. redLUT.length,
  756. redLUT,
  757. greenLUT,
  758. blueLUT,
  759. alphaLUT);
  760. }
  761. if ((bits == 8 && dataType == DataBuffer.TYPE_BYTE) ||
  762. (bits == 16 &&
  763. (dataType == DataBuffer.TYPE_SHORT ||
  764. dataType == DataBuffer.TYPE_USHORT))) {
  765. int[] bandOffsets = { 0 };
  766. this.sampleModel =
  767. new PixelInterleavedSampleModel(dataType,
  768. 1, 1, 1, 1,
  769. bandOffsets);
  770. } else {
  771. this.sampleModel =
  772. new MultiPixelPackedSampleModel(dataType, 1, 1, bits);
  773. }
  774. }
  775. }
  776. /**
  777. * Returns a specifier for an indexed-color image format that will pack
  778. * index values of the given bit depth into array elements of
  779. * the specified data type.
  780. *
  781. * @param redLUT an array of <code>byte</code>s containing
  782. * the red values for each index.
  783. * @param greenLUT an array of <code>byte</code>s containing * the
  784. * green values for each index.
  785. * @param blueLUT an array of <code>byte</code>s containing the
  786. * blue values for each index.
  787. * @param alphaLUT an array of <code>byte</code>s containing the
  788. * alpha values for each index, or <code>null</code> to create a
  789. * fully opaque LUT.
  790. * @param bits the number of bits in each index.
  791. * @param dataType the desired output type, as one of the enumerations
  792. * from the <code>DataBuffer</code> class.
  793. *
  794. * @return an <code>ImageTypeSpecifier</code> with the desired
  795. * characteristics.
  796. *
  797. * @exception IllegalArgumentException if <code>redLUT</code> is
  798. * <code>null</code>.
  799. * @exception IllegalArgumentException if <code>greenLUT</code> is
  800. * <code>null</code>.
  801. * @exception IllegalArgumentException if <code>blueLUT</code> is
  802. * <code>null</code>.
  803. * @exception IllegalArgumentException if <code>bits</code> is
  804. * not one of 1, 2, 4, 8, or 16.
  805. * @exception IllegalArgumentException if the
  806. * non-<code>null</code> LUT parameters do not have lengths of
  807. * exactly <code>1 << bits</code>.
  808. * @exception IllegalArgumentException if <code>dataType</code> is
  809. * not one of <code>DataBuffer.TYPE_BYTE</code>,
  810. * <code>DataBuffer.TYPE_SHORT</code>, <code>TYPE_USHORT</code>,
  811. * or <code>TYPE_INT</code>.
  812. * @exception IllegalArgumentException if <code>bits</code> is
  813. * larger than the bit size of the given <code>dataType</code>.
  814. */
  815. public static ImageTypeSpecifier
  816. createIndexed(byte[] redLUT,
  817. byte[] greenLUT,
  818. byte[] blueLUT,
  819. byte[] alphaLUT,
  820. int bits,
  821. int dataType) {
  822. return new ImageTypeSpecifier.Indexed(redLUT,
  823. greenLUT,
  824. blueLUT,
  825. alphaLUT,
  826. bits,
  827. dataType);
  828. }
  829. /**
  830. * Returns an <code>ImageTypeSpecifier</code> that encodes
  831. * one of the standard <code>BufferedImage</code> types
  832. * (other than <code>TYPE_CUSTOM</code>).
  833. *
  834. * @param bufferedImageType an int representing one of the standard
  835. * <code>BufferedImage</code> types.
  836. *
  837. * @return an <code>ImageTypeSpecifier</code> with the desired
  838. * characteristics.
  839. *
  840. * @exception IllegalArgumentException if
  841. * <code>bufferedImageType</code> is not one of the standard
  842. * types, or is equal to <code>TYPE_CUSTOM</code>.
  843. *
  844. * @see java.awt.image.BufferedImage
  845. * @see java.awt.image.BufferedImage#TYPE_INT_RGB
  846. * @see java.awt.image.BufferedImage#TYPE_INT_ARGB
  847. * @see java.awt.image.BufferedImage#TYPE_INT_ARGB_PRE
  848. * @see java.awt.image.BufferedImage#TYPE_INT_BGR
  849. * @see java.awt.image.BufferedImage#TYPE_3BYTE_BGR
  850. * @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR
  851. * @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR_PRE
  852. * @see java.awt.image.BufferedImage#TYPE_USHORT_565_RGB
  853. * @see java.awt.image.BufferedImage#TYPE_USHORT_555_RGB
  854. * @see java.awt.image.BufferedImage#TYPE_BYTE_GRAY
  855. * @see java.awt.image.BufferedImage#TYPE_USHORT_GRAY
  856. * @see java.awt.image.BufferedImage#TYPE_BYTE_BINARY
  857. * @see java.awt.image.BufferedImage#TYPE_BYTE_INDEXED
  858. */
  859. public static
  860. ImageTypeSpecifier createFromBufferedImageType(int bufferedImageType) {
  861. if (bufferedImageType >= BufferedImage.TYPE_INT_RGB &&
  862. bufferedImageType <= BufferedImage.TYPE_BYTE_INDEXED) {
  863. return BISpecifier[bufferedImageType];
  864. } else if (bufferedImageType == BufferedImage.TYPE_CUSTOM) {
  865. throw new IllegalArgumentException("Cannot create from TYPE_CUSTOM!");
  866. } else {
  867. throw new IllegalArgumentException("Invalid BufferedImage type!");
  868. }
  869. }
  870. /**
  871. * Returns an <code>ImageTypeSpecifier</code> that encodes the
  872. * layout of a <code>RenderedImage</code> (which may be a
  873. * <code>BufferedImage</code>).
  874. *
  875. * @param image a <code>RenderedImage</code>.
  876. *
  877. * @return an <code>ImageTypeSpecifier</code> with the desired
  878. * characteristics.
  879. *
  880. * @exception IllegalArgumentException if <code>image</code> is
  881. * <code>null</code>.
  882. */
  883. public static
  884. ImageTypeSpecifier createFromRenderedImage(RenderedImage image) {
  885. if (image == null) {
  886. throw new IllegalArgumentException("image == null!");
  887. }
  888. if (image instanceof BufferedImage) {
  889. int bufferedImageType = ((BufferedImage)image).getType();
  890. if (bufferedImageType != BufferedImage.TYPE_CUSTOM) {
  891. return BISpecifier[bufferedImageType];
  892. }
  893. }
  894. return new ImageTypeSpecifier(image);
  895. }
  896. /**
  897. * Returns an int containing one of the enumerated constant values
  898. * describing image formats from <code>BufferedImage</code>.
  899. *
  900. * @return an <code>int</code> representing a
  901. * <code>BufferedImage</code> type.
  902. *
  903. * @see java.awt.image.BufferedImage
  904. * @see java.awt.image.BufferedImage#TYPE_CUSTOM
  905. * @see java.awt.image.BufferedImage#TYPE_INT_RGB
  906. * @see java.awt.image.BufferedImage#TYPE_INT_ARGB
  907. * @see java.awt.image.BufferedImage#TYPE_INT_ARGB_PRE
  908. * @see java.awt.image.BufferedImage#TYPE_INT_BGR
  909. * @see java.awt.image.BufferedImage#TYPE_3BYTE_BGR
  910. * @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR
  911. * @see java.awt.image.BufferedImage#TYPE_4BYTE_ABGR_PRE
  912. * @see java.awt.image.BufferedImage#TYPE_USHORT_565_RGB
  913. * @see java.awt.image.BufferedImage#TYPE_USHORT_555_RGB
  914. * @see java.awt.image.BufferedImage#TYPE_BYTE_GRAY
  915. * @see java.awt.image.BufferedImage#TYPE_USHORT_GRAY
  916. * @see java.awt.image.BufferedImage#TYPE_BYTE_BINARY
  917. * @see java.awt.image.BufferedImage#TYPE_BYTE_INDEXED
  918. */
  919. public int getBufferedImageType() {
  920. BufferedImage bi = createBufferedImage(1, 1);
  921. return bi.getType();
  922. }
  923. /**
  924. * Return the number of color components
  925. * specified by this object. This is the same value as returned by
  926. * <code>ColorModel.getNumComponents</code>
  927. *
  928. * @return the number of components in the image.
  929. */
  930. public int getNumComponents() {
  931. return colorModel.getNumComponents();
  932. }
  933. /**
  934. * Return the number of bands
  935. * specified by this object. This is the same value as returned by
  936. * <code>SampleModel.getNumBands</code>
  937. *
  938. * @return the number of bands in the image.
  939. */
  940. public int getNumBands() {
  941. return sampleModel.getNumBands();
  942. }
  943. /**
  944. * Return the number of bits used to represent samples of the given band.
  945. *
  946. * @param band the index of the band to be queried, as an
  947. * int.
  948. *
  949. * @return an int specifying a number of bits.
  950. *
  951. * @exception IllegalArgumentException if <code>band</code> is
  952. * negative or greater than the largest band index.
  953. */
  954. public int getBitsPerBand(int band) {
  955. if (band < 0 | band >= getNumBands()) {
  956. throw new IllegalArgumentException("band out of range!");
  957. }
  958. return sampleModel.getSampleSize(band);
  959. }
  960. /**
  961. * Returns a <code>SampleModel</code> based on the settings
  962. * encapsulated within this object. The width and height of the
  963. * <code>SampleModel</code> will be set to arbitrary values.
  964. *
  965. * @return a <code>SampleModel</code> with arbitrary dimensions.
  966. */
  967. public SampleModel getSampleModel() {
  968. return sampleModel;
  969. }
  970. /**
  971. * Returns a <code>SampleModel</code> based on the settings
  972. * encapsulated within this object. The width and height of the
  973. * <code>SampleModel</code> will be set to the supplied values.
  974. *
  975. * @param width the desired width of the returned <code>SampleModel</code>.
  976. * @param height the desired height of the returned
  977. * <code>SampleModel</code>.
  978. *
  979. * @return a <code>SampleModel</code> with the given dimensions.
  980. *
  981. * @exception IllegalArgumentException if either <code>width</code> or
  982. * <code>height</code> are negative or zero.
  983. * @exception IllegalArgumentException if the product of
  984. * <code>width</code> and <code>height</code> is greater than
  985. * <code>Integer.MAX_VALUE</code>
  986. */
  987. public SampleModel getSampleModel(int width, int height) {
  988. if ((long)width*height > Integer.MAX_VALUE) {
  989. throw new IllegalArgumentException
  990. ("width*height > Integer.MAX_VALUE!");
  991. }
  992. return sampleModel.createCompatibleSampleModel(width, height);
  993. }
  994. /**
  995. * Returns the <code>ColorModel</code> specified by this object.
  996. *
  997. * @return a <code>ColorModel</code>.
  998. */
  999. public ColorModel getColorModel() {
  1000. return colorModel;
  1001. }
  1002. /**
  1003. * Creates a <code>BufferedImage</code> with a given width and
  1004. * height according to the specification embodied in this object.
  1005. *
  1006. * @param width the desired width of the returned
  1007. * <code>BufferedImage</code>.
  1008. * @param height the desired height of the returned
  1009. * <code>BufferedImage</code>.
  1010. *
  1011. * @return a new <code>BufferedImage</code>
  1012. *
  1013. * @exception IllegalArgumentException if either <code>width</code> or
  1014. * <code>height</code> are negative or zero.
  1015. * @exception IllegalArgumentException if the product of
  1016. * <code>width</code> and <code>height</code> is greater than
  1017. * <code>Integer.MAX_VALUE</code>, or if the number of array
  1018. * elements needed to store the image is greater than
  1019. * <code>Integer.MAX_VALUE</code>.
  1020. */
  1021. public BufferedImage createBufferedImage(int width, int height) {
  1022. try {
  1023. SampleModel sampleModel = getSampleModel(width, height);
  1024. WritableRaster raster =
  1025. Raster.createWritableRaster(sampleModel,
  1026. new Point(0, 0));
  1027. return new BufferedImage(colorModel, raster,
  1028. colorModel.isAlphaPremultiplied(),
  1029. new Hashtable());
  1030. } catch (NegativeArraySizeException e) {
  1031. // Exception most likely thrown from a DataBuffer constructor
  1032. throw new IllegalArgumentException
  1033. ("Array size > Integer.MAX_VALUE!");
  1034. }
  1035. }
  1036. /**
  1037. * Returns <code>true</code> if the given <code>Object</code> is
  1038. * an <code>ImageTypeSpecifier</code> and has a
  1039. * <code>SampleModel</code> and <code>ColorModel</code> that are
  1040. * equal to those of this object.
  1041. *
  1042. * @param o the <code>Object</code> to be compared for equality.
  1043. *
  1044. * @return <code>true</code> if the given object is an equivalent
  1045. * <code>ImageTypeSpecifier</code>.
  1046. */
  1047. public boolean equals(Object o) {
  1048. if ((o == null) || !(o instanceof ImageTypeSpecifier)) {
  1049. return false;
  1050. }
  1051. ImageTypeSpecifier that = (ImageTypeSpecifier)o;
  1052. return (colorModel.equals(that.colorModel)) &&
  1053. (sampleModel.equals(that.sampleModel));
  1054. }
  1055. }