1. /*
  2. * @(#)IIOParam.java 1.29 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.Rectangle;
  10. /**
  11. * A superclass of all classes describing how streams should be
  12. * decoded or encoded. This class contains all the variables and
  13. * methods that are shared by <code>ImageReadParam</code> and
  14. * <code>ImageWriteParam</code>.
  15. *
  16. * <p> This class provides mechanisms to specify a source region and a
  17. * destination region. When reading, the source is the stream and
  18. * the in-memory image is the destination. When writing, these are
  19. * reversed. In the case of writing, destination regions may be used
  20. * only with a writer that supports pixel replacement.
  21. * <p>
  22. * Decimation subsampling may be specified for both readers
  23. * and writers, using a movable subsampling grid.
  24. * <p>
  25. * Subsets of the source and destination bands may be selected.
  26. *
  27. * @version 0.5
  28. */
  29. public abstract class IIOParam {
  30. /**
  31. * The source region, on <code>null</code> if none is set.
  32. */
  33. protected Rectangle sourceRegion = null;
  34. /**
  35. * The decimation subsampling to be applied in the horizontal
  36. * direction. By default, the value is <code>1</code>.
  37. * The value must not be negative or 0.
  38. */
  39. protected int sourceXSubsampling = 1;
  40. /**
  41. * The decimation subsampling to be applied in the vertical
  42. * direction. By default, the value is <code>1</code>.
  43. * The value must not be negative or 0.
  44. */
  45. protected int sourceYSubsampling = 1;
  46. /**
  47. * A horizontal offset to be applied to the subsampling grid before
  48. * subsampling. The first pixel to be used will be offset this
  49. * amount from the origin of the region, or of the image if no
  50. * region is specified.
  51. */
  52. protected int subsamplingXOffset = 0;
  53. /**
  54. * A vertical offset to be applied to the subsampling grid before
  55. * subsampling. The first pixel to be used will be offset this
  56. * amount from the origin of the region, or of the image if no
  57. * region is specified.
  58. */
  59. protected int subsamplingYOffset = 0;
  60. /**
  61. * An array of <code>int</code>s indicating which source bands
  62. * will be used, or <code>null</code>. If <code>null</code>, the
  63. * set of source bands to be used is as described in the comment
  64. * for the <code>setSourceBands</code> method. No value should
  65. * be allowed to be negative.
  66. */
  67. protected int[] sourceBands = null;
  68. /**
  69. * An <code>ImageTypeSpecifier</code> to be used to generate a
  70. * destination image when reading, or to set the output color type
  71. * when writing. If non has been setm the value will be
  72. * <code>null</code>. By default, the value is <code>null</code>.
  73. */
  74. protected ImageTypeSpecifier destinationType = null;
  75. /**
  76. * The offset in the destination where the upper-left decoded
  77. * pixel should be placed. By default, the value is (0, 0).
  78. */
  79. protected Point destinationOffset = new Point(0, 0);
  80. /**
  81. * The default <code>IIOParamController</code> that will be
  82. * used to provide settings for this <code>IIOParam</code>
  83. * object when the <code>activateController</code> method
  84. * is called. This default should be set by subclasses
  85. * that choose to provide their own default controller,
  86. * usually a GUI, for setting parameters.
  87. *
  88. * @see IIOParamController
  89. * @see #getDefaultController
  90. * @see #activateController
  91. */
  92. protected IIOParamController defaultController = null;
  93. /**
  94. * The <code>IIOParamController</code> that will be
  95. * used to provide settings for this <code>IIOParam</code>
  96. * object when the <code>activateController</code> method
  97. * is called. This value overrides any default controller,
  98. * even when null.
  99. *
  100. * @see IIOParamController
  101. * @see #setController(IIOParamController)
  102. * @see #hasController()
  103. * @see #activateController()
  104. */
  105. protected IIOParamController controller = null;
  106. /**
  107. * Protected constructor may be called only by subclasses.
  108. */
  109. protected IIOParam() {
  110. controller = defaultController;
  111. }
  112. /**
  113. * Sets the source region of interest. The region of interest is
  114. * described as a rectangle, with the upper-left corner of the
  115. * source image as pixel (0, 0) and increasing values down and to
  116. * the right. The actual number of pixels used will depend on
  117. * the subsampling factors set by <code>setSourceSubsampling</code>.
  118. * If subsampling has been set such that this number is zero,
  119. * an <code>IllegalStateException</code> will be thrown.
  120. *
  121. * <p> The source region of interest specified by this method will
  122. * be clipped as needed to fit within the source bounds, as well
  123. * as the destination offsets, width, and height at the time of
  124. * actual I/O.
  125. *
  126. * <p> A value of <code>null</code> for <code>sourceRegion</code>
  127. * will remove any region specification, causing the entire image
  128. * to be used.
  129. *
  130. * @param sourceRegion a <code>Rectangle</code> specifying the
  131. * source region of interest, or <code>null</code>.
  132. *
  133. * @exception IllegalArgumentException if
  134. * <code>sourceRegion</code> is non-<code>null</code> and either
  135. * <code>sourceRegion.x</code> or <code>sourceRegion.y</code> is
  136. * negative.
  137. * @exception IllegalArgumentException if
  138. * <code>sourceRegion</code> is non-<code>null</code> and either
  139. * <code>sourceRegion.width</code> or
  140. * <code>sourceRegion.height</code> is negative or 0.
  141. * @exception IllegalStateException if subsampling is such that
  142. * this region will have a subsampled width or height of zero.
  143. *
  144. * @see #getSourceRegion
  145. * @see #setSourceSubsampling
  146. * @see ImageReadParam#setDestinationOffset
  147. * @see ImageReadParam#getDestinationOffset
  148. */
  149. public void setSourceRegion(Rectangle sourceRegion) {
  150. if (sourceRegion == null) {
  151. this.sourceRegion = null;
  152. return;
  153. }
  154. if (sourceRegion.x < 0) {
  155. throw new IllegalArgumentException("sourceRegion.x < 0!");
  156. }
  157. if (sourceRegion.y < 0){
  158. throw new IllegalArgumentException("sourceRegion.y < 0!");
  159. }
  160. if (sourceRegion.width <= 0) {
  161. throw new IllegalArgumentException("sourceRegion.width <= 0!");
  162. }
  163. if (sourceRegion.height <= 0) {
  164. throw new IllegalArgumentException("sourceRegion.height <= 0!");
  165. }
  166. // Throw an IllegalStateException if region falls between subsamples
  167. if (sourceRegion.width <= subsamplingXOffset) {
  168. throw new IllegalStateException
  169. ("sourceRegion.width <= subsamplingXOffset!");
  170. }
  171. if (sourceRegion.height <= subsamplingYOffset) {
  172. throw new IllegalStateException
  173. ("sourceRegion.height <= subsamplingYOffset!");
  174. }
  175. this.sourceRegion = (Rectangle)sourceRegion.clone();
  176. }
  177. /**
  178. * Returns the source region to be used. The returned value is
  179. * that set by the most recent call to
  180. * <code>setSourceRegion</code>, and will be <code>null</code> if
  181. * there is no region set.
  182. *
  183. * @return the source region of interest as a
  184. * <code>Rectangle</code>, or <code>null</code>.
  185. *
  186. * @see #setSourceRegion
  187. */
  188. public Rectangle getSourceRegion() {
  189. if (sourceRegion == null) {
  190. return null;
  191. }
  192. return (Rectangle)sourceRegion.clone();
  193. }
  194. /**
  195. * Specifies a decimation subsampling to apply on I/O. The
  196. * <code>sourceXSubsampling</code> and
  197. * <code>sourceYSubsampling</code> parameters specify the
  198. * subsampling period (<i>i.e.</i>, the number of rows and columns
  199. * to advance after every source pixel). Specifically, a period of
  200. * 1 will use every row or column; a period of 2 will use every
  201. * other row or column. The <code>subsamplingXOffset</code> and
  202. * <code>subsamplingYOffset</code> parameters specify an offset
  203. * from the region (or image) origin for the first subsampled pixel.
  204. * Adjusting the origin of the subsample grid is useful for avoiding
  205. * seams when subsampling a very large source image into destination
  206. * regions that will be assembled into a complete subsampled image.
  207. * Most users will want to simply leave these parameters at 0.
  208. *
  209. * <p> The number of pixels and scanlines to be used are calculated
  210. * as follows.
  211. * <p>
  212. * The number of subsampled pixels in a scanline is given by
  213. * <p>
  214. * <code>truncate[(width - subsamplingXOffset + sourceXSubsampling - 1)
  215. * / sourceXSubsampling]</code>.
  216. * <p>
  217. * If the region is such that this width is zero, an
  218. * <code>IllegalStateException</code> is thrown.
  219. * <p>
  220. * The number of scanlines to be used can be computed similarly.
  221. *
  222. * <p>The ability to set the subsampling grid to start somewhere
  223. * other than the source region origin is useful if the
  224. * region is being used to create subsampled tiles of a large image,
  225. * where the tile width and height are not multiples of the
  226. * subsampling periods. If the subsampling grid does not remain
  227. * consistent from tile to tile, there will be artifacts at the tile
  228. * boundaries. By adjusting the subsampling grid offset for each
  229. * tile to compensate, these artifacts can be avoided. The tradeoff
  230. * is that in order to avoid these artifacts, the tiles are not all
  231. * the same size. The grid offset to use in this case is given by:
  232. * <br>
  233. * grid offset = [period - (region offset modulo period)] modulo period)
  234. *
  235. * <p> If either <code>sourceXSubsampling</code> or
  236. * <code>sourceYSubsampling</code> is 0 or negative, an
  237. * <code>IllegalArgumentException</code> will be thrown.
  238. *
  239. * <p> If either <code>subsamplingXOffset</code> or
  240. * <code>subsamplingYOffset</code> is negative or greater than or
  241. * equal to the corresponding period, an
  242. * <code>IllegalArgumentException</code> will be thrown.
  243. *
  244. * <p> There is no <code>unsetSourceSubsampling</code> method;
  245. * simply call <code>setSourceSubsampling(1, 1, 0, 0)</code> to
  246. * restore default values.
  247. *
  248. * @param sourceXSubsampling the number of columns to advance
  249. * between pixels.
  250. * @param sourceYSubsampling the number of rows to advance between
  251. * pixels.
  252. * @param subsamplingXOffset the horizontal offset of the first subsample
  253. * within the region, or within the image if no region is set.
  254. * @param subsamplingYOffset the horizontal offset of the first subsample
  255. * within the region, or within the image if no region is set.
  256. * @exception IllegalArgumentException if either period is
  257. * negative or 0, or if either grid offset is negative or greater than
  258. * the corresponding period.
  259. * @exception IllegalStateException if the source region is such that
  260. * the subsampled output would contain no pixels.
  261. */
  262. public void setSourceSubsampling(int sourceXSubsampling,
  263. int sourceYSubsampling,
  264. int subsamplingXOffset,
  265. int subsamplingYOffset) {
  266. if (sourceXSubsampling <= 0) {
  267. throw new IllegalArgumentException("sourceXSubsampling <= 0!");
  268. }
  269. if (sourceYSubsampling <= 0) {
  270. throw new IllegalArgumentException("sourceYSubsampling <= 0!");
  271. }
  272. if (subsamplingXOffset < 0 ||
  273. subsamplingXOffset >= sourceXSubsampling) {
  274. throw new IllegalArgumentException
  275. ("subsamplingXOffset out of range!");
  276. }
  277. if (subsamplingYOffset < 0 ||
  278. subsamplingYOffset >= sourceYSubsampling) {
  279. throw new IllegalArgumentException
  280. ("subsamplingYOffset out of range!");
  281. }
  282. // Throw an IllegalStateException if region falls between subsamples
  283. if (sourceRegion != null) {
  284. if (subsamplingXOffset >= sourceRegion.width ||
  285. subsamplingYOffset >= sourceRegion.height) {
  286. throw new IllegalStateException("region contains no pixels!");
  287. }
  288. }
  289. this.sourceXSubsampling = sourceXSubsampling;
  290. this.sourceYSubsampling = sourceYSubsampling;
  291. this.subsamplingXOffset = subsamplingXOffset;
  292. this.subsamplingYOffset = subsamplingYOffset;
  293. }
  294. /**
  295. * Returns the number of source columns to advance for each pixel.
  296. *
  297. * <p>If <code>setSourceSubsampling</code> has not been called, 1
  298. * is returned (which is the correct value).
  299. *
  300. * @return the source subsampling X period.
  301. *
  302. * @see #setSourceSubsampling
  303. * @see #getSourceYSubsampling
  304. */
  305. public int getSourceXSubsampling() {
  306. return sourceXSubsampling;
  307. }
  308. /**
  309. * Returns the number of rows to advance for each pixel.
  310. *
  311. * <p>If <code>setSourceSubsampling</code> has not been called, 1
  312. * is returned (which is the correct value).
  313. *
  314. * @return the source subsampling Y period.
  315. *
  316. * @see #setSourceSubsampling
  317. * @see #getSourceXSubsampling
  318. */
  319. public int getSourceYSubsampling() {
  320. return sourceYSubsampling;
  321. }
  322. /**
  323. * Returns the horizontal offset of the subsampling grid.
  324. *
  325. * <p>If <code>setSourceSubsampling</code> has not been called, 0
  326. * is returned (which is the correct value).
  327. *
  328. * @return the source subsampling grid X offset.
  329. *
  330. * @see #setSourceSubsampling
  331. * @see #getSubsamplingYOffset
  332. */
  333. public int getSubsamplingXOffset() {
  334. return subsamplingXOffset;
  335. }
  336. /**
  337. * Returns the vertical offset of the subsampling grid.
  338. *
  339. * <p>If <code>setSourceSubsampling</code> has not been called, 0
  340. * is returned (which is the correct value).
  341. *
  342. * @return the source subsampling grid Y offset.
  343. *
  344. * @see #setSourceSubsampling
  345. * @see #getSubsamplingXOffset
  346. */
  347. public int getSubsamplingYOffset() {
  348. return subsamplingYOffset;
  349. }
  350. /**
  351. * Sets the indices of the source bands to be used. Duplicate
  352. * indices are not allowed.
  353. *
  354. * <p> A <code>null</code> value indicates that all source bands
  355. * will be used.
  356. *
  357. * <p> At the time of reading, an
  358. * <code>IllegalArgumentException</code> will be thrown by the
  359. * reader or writer if a value larger than the largest available
  360. * source band index has been specified or if the number of source
  361. * bands and destination bands to be used differ. The
  362. * <code>ImageReader.checkReadParamBandSettings</code> method may
  363. * be used to automate this test.
  364. *
  365. * <p> Semantically, a copy is made of the array; changes to the
  366. * array contents subsequent to this call have no effect on
  367. * this <code>IIOParam</code>.
  368. *
  369. * @param sourceBands an array of integer band indices to be
  370. * used.
  371. *
  372. * @exception IllegalArgumentException if <code>sourceBands</code>
  373. * contains a negative or duplicate value.
  374. *
  375. * @see #getSourceBands
  376. * @see ImageReadParam#setDestinationBands
  377. * @see ImageReader#checkReadParamBandSettings
  378. */
  379. public void setSourceBands(int[] sourceBands) {
  380. if (sourceBands == null) {
  381. this.sourceBands = null;
  382. } else {
  383. int numBands = sourceBands.length;
  384. for (int i = 0; i < numBands; i++) {
  385. int band = sourceBands[i];
  386. if (band < 0) {
  387. throw new IllegalArgumentException("Band value < 0!");
  388. }
  389. for (int j = i + 1; j < numBands; j++) {
  390. if (band == sourceBands[j]) {
  391. throw new IllegalArgumentException("Duplicate band value!");
  392. }
  393. }
  394. }
  395. this.sourceBands = (int[])(sourceBands.clone());
  396. }
  397. }
  398. /**
  399. * Returns the set of of source bands to be used. The returned
  400. * value is that set by the most recent call to
  401. * <code>setSourceBands</code>, or <code>null</code> if there have
  402. * been no calls to <code>setSourceBands</code>.
  403. *
  404. * <p> Semantically, the array returned is a copy; changes to
  405. * array contents subsequent to this call have no effect on this
  406. * <code>IIOParam</code>.
  407. *
  408. * @return the set of source bands to be used, or
  409. * <code>null</code>.
  410. *
  411. * @see #setSourceBands
  412. */
  413. public int[] getSourceBands() {
  414. if (sourceBands == null) {
  415. return null;
  416. }
  417. return (int[])(sourceBands.clone());
  418. }
  419. /**
  420. * Sets the desired image type for the destination image, using an
  421. * <code>ImageTypeSpecifier</code>.
  422. *
  423. * <p> When reading, if the layout of the destination has been set
  424. * using this method, each call to an <code>ImageReader</code>
  425. * <code>read</code> method will return a new
  426. * <code>BufferedImage</code> using the format specified by the
  427. * supplied type specifier. As a side effect, any destination
  428. * <code>BufferedImage</code> set by
  429. * <code>ImageReadParam.setDestination(BufferedImage)</code> will
  430. * no longer be set as the destination. In other words, this
  431. * method may be thought of as calling
  432. * <code>setDestination((BufferedImage)null)</code>.
  433. *
  434. * <p> When writing, the destination type maybe used to determine
  435. * the color type of the image. The <code>SampleModel</code>
  436. * information will be ignored, and may be <code>null</code>. For
  437. * example, a 4-banded image could represent either CMYK or RGBA
  438. * data. If a destination type is set, its
  439. * <code>ColorModel</code> will override any
  440. * <code>ColorModel</code> on the image itself. This is crucial
  441. * when <code>setSourceBands</code> is used since the image's
  442. * <code>ColorModel</code> will refer to the entire image rather
  443. * than to the subset of bands being written.
  444. *
  445. * @param destinationType the <code>ImageTypeSpecifier</code> to
  446. * be used to determine the destination layout and color type.
  447. *
  448. * @see #getDestinationType
  449. */
  450. public void setDestinationType(ImageTypeSpecifier destinationType) {
  451. this.destinationType = destinationType;
  452. }
  453. /**
  454. * Returns the type of image to be returned by the read, if one
  455. * was set by a call to
  456. * <code>setDestination(ImageTypeSpecifier)</code>, as an
  457. * <code>ImageTypeSpecifier</code>. If none was set,
  458. * <code>null</code> is returned.
  459. *
  460. * @return an <code>ImageTypeSpecifier</code> describing the
  461. * destination type, or <code>null</code>.
  462. *
  463. * @see #setDestinationType
  464. */
  465. public ImageTypeSpecifier getDestinationType() {
  466. return destinationType;
  467. }
  468. /**
  469. * Specifies the offset in the destination image at which future
  470. * decoded pixels are to be placed, when reading, or where a
  471. * region will be written, when writing.
  472. *
  473. * <p> When reading, the region to be written within the
  474. * destination <code>BufferedImage</code> will start at this
  475. * offset and have a width and height determined by the source
  476. * region of interest, the subsampling parameters, and the
  477. * destination bounds.
  478. *
  479. * <p> Normal writes are not affected by this method, only writes
  480. * performed using <code>ImageWriter.replacePixels</code>. For
  481. * such writes, the offset specified is within the output stream
  482. * image whose pixels are being modified.
  483. *
  484. * <p> There is no <code>unsetDestinationOffset</code> method;
  485. * simply call <code>setDestinationOffset(new Point(0, 0))</code> to
  486. * restore default values.
  487. *
  488. * @param destinationOffset the offset in the destination, as a
  489. * <code>Point</code>.
  490. *
  491. * @exception IllegalArgumentException if
  492. * <code>destinationOffset</code> is <code>null</code>.
  493. *
  494. * @see #getDestinationOffset
  495. * @see ImageWriter#replacePixels
  496. */
  497. public void setDestinationOffset(Point destinationOffset) {
  498. if (destinationOffset == null) {
  499. throw new IllegalArgumentException("destinationOffset == null!");
  500. }
  501. this.destinationOffset = (Point)destinationOffset.clone();
  502. }
  503. /**
  504. * Returns the offset in the destination image at which pixels are
  505. * to be placed.
  506. *
  507. * <p> If <code>setDestinationOffsets</code> has not been called,
  508. * a <code>Point</code> with zero X and Y values is returned
  509. * (which is the correct value).
  510. *
  511. * @return the destination offset as a <code>Point</code>.
  512. *
  513. * @see #setDestinationOffset
  514. */
  515. public Point getDestinationOffset() {
  516. return (Point)destinationOffset.clone();
  517. }
  518. /**
  519. * Sets the <code>IIOParamController</code> to be used
  520. * to provide settings for this <code>IIOParam</code>
  521. * object when the <code>activateController</code> method
  522. * is called, overriding any default controller. If the
  523. * argument is <code>null</code>, no controller will be
  524. * used, including any default. To restore the default, use
  525. * <code>setController(getDefaultController())</code>.
  526. *
  527. * @param controller An appropriate
  528. * <code>IIOParamController</code>, or <code>null</code>.
  529. *
  530. * @see IIOParamController
  531. * @see #getController
  532. * @see #getDefaultController
  533. * @see #hasController
  534. * @see #activateController()
  535. */
  536. public void setController(IIOParamController controller) {
  537. this.controller = controller;
  538. }
  539. /**
  540. * Returns whatever <code>IIOParamController</code> is currently
  541. * installed. This could be the default if there is one,
  542. * <code>null</code>, or the argument of the most recent call
  543. * to <code>setController</code>.
  544. *
  545. * @return the currently installed
  546. * <code>IIOParamController</code>, or <code>null</code>.
  547. *
  548. * @see IIOParamController
  549. * @see #setController
  550. * @see #getDefaultController
  551. * @see #hasController
  552. * @see #activateController()
  553. */
  554. public IIOParamController getController() {
  555. return controller;
  556. }
  557. /**
  558. * Returns the default <code>IIOParamController</code>, if there
  559. * is one, regardless of the currently installed controller. If
  560. * there is no default controller, returns <code>null</code>.
  561. *
  562. * @return the default <code>IIOParamController</code>, or
  563. * <code>null</code>.
  564. *
  565. * @see IIOParamController
  566. * @see #setController(IIOParamController)
  567. * @see #getController
  568. * @see #hasController
  569. * @see #activateController()
  570. */
  571. public IIOParamController getDefaultController() {
  572. return defaultController;
  573. }
  574. /**
  575. * Returns <code>true</code> if there is a controller installed
  576. * for this <code>IIOParam</code> object. This will return
  577. * <code>true</code> if <code>getController</code> would not
  578. * return <code>null</code>.
  579. *
  580. * @return <code>true</code> if a controller is installed.
  581. *
  582. * @see IIOParamController
  583. * @see #setController(IIOParamController)
  584. * @see #getController
  585. * @see #getDefaultController
  586. * @see #activateController()
  587. */
  588. public boolean hasController() {
  589. return (controller != null);
  590. }
  591. /**
  592. * Activates the installed <code>IIOParamController</code> for
  593. * this <code>IIOParam</code> object and returns the resulting
  594. * value. When this method returns <code>true</code>, all values
  595. * for this <code>IIOParam</code> object will be ready for the
  596. * next read or write operation. If <code>false</code> is
  597. * returned, no settings in this object will have been disturbed
  598. * (<i>i.e.</i>, the user canceled the operation).
  599. *
  600. * <p> Ordinarily, the controller will be a GUI providing a user
  601. * interface for a subclass of <code>IIOParam</code> for a
  602. * particular plug-in. Controllers need not be GUIs, however.
  603. *
  604. * @return <code>true</code> if the controller completed normally.
  605. *
  606. * @exception IllegalStateException if there is no controller
  607. * currently installed.
  608. *
  609. * @see IIOParamController
  610. * @see #setController(IIOParamController)
  611. * @see #getController
  612. * @see #getDefaultController
  613. * @see #hasController
  614. */
  615. public boolean activateController() {
  616. if (!hasController()) {
  617. throw new IllegalStateException("hasController() == false!");
  618. }
  619. return getController().activate(this);
  620. }
  621. }