001 /*
002 * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.imageio;
027
028 import java.awt.Point;
029 import java.awt.Rectangle;
030
031 /**
032 * A superclass of all classes describing how streams should be
033 * decoded or encoded. This class contains all the variables and
034 * methods that are shared by <code>ImageReadParam</code> and
035 * <code>ImageWriteParam</code>.
036 *
037 * <p> This class provides mechanisms to specify a source region and a
038 * destination region. When reading, the source is the stream and
039 * the in-memory image is the destination. When writing, these are
040 * reversed. In the case of writing, destination regions may be used
041 * only with a writer that supports pixel replacement.
042 * <p>
043 * Decimation subsampling may be specified for both readers
044 * and writers, using a movable subsampling grid.
045 * <p>
046 * Subsets of the source and destination bands may be selected.
047 *
048 * @version 0.5
049 */
050 public abstract class IIOParam {
051
052 /**
053 * The source region, on <code>null</code> if none is set.
054 */
055 protected Rectangle sourceRegion = null;
056
057 /**
058 * The decimation subsampling to be applied in the horizontal
059 * direction. By default, the value is <code>1</code>.
060 * The value must not be negative or 0.
061 */
062 protected int sourceXSubsampling = 1;
063
064 /**
065 * The decimation subsampling to be applied in the vertical
066 * direction. By default, the value is <code>1</code>.
067 * The value must not be negative or 0.
068 */
069 protected int sourceYSubsampling = 1;
070
071 /**
072 * A horizontal offset to be applied to the subsampling grid before
073 * subsampling. The first pixel to be used will be offset this
074 * amount from the origin of the region, or of the image if no
075 * region is specified.
076 */
077 protected int subsamplingXOffset = 0;
078
079 /**
080 * A vertical offset to be applied to the subsampling grid before
081 * subsampling. The first pixel to be used will be offset this
082 * amount from the origin of the region, or of the image if no
083 * region is specified.
084 */
085 protected int subsamplingYOffset = 0;
086
087 /**
088 * An array of <code>int</code>s indicating which source bands
089 * will be used, or <code>null</code>. If <code>null</code>, the
090 * set of source bands to be used is as described in the comment
091 * for the <code>setSourceBands</code> method. No value should
092 * be allowed to be negative.
093 */
094 protected int[] sourceBands = null;
095
096 /**
097 * An <code>ImageTypeSpecifier</code> to be used to generate a
098 * destination image when reading, or to set the output color type
099 * when writing. If non has been setm the value will be
100 * <code>null</code>. By default, the value is <code>null</code>.
101 */
102 protected ImageTypeSpecifier destinationType = null;
103
104 /**
105 * The offset in the destination where the upper-left decoded
106 * pixel should be placed. By default, the value is (0, 0).
107 */
108 protected Point destinationOffset = new Point(0, 0);
109
110 /**
111 * The default <code>IIOParamController</code> that will be
112 * used to provide settings for this <code>IIOParam</code>
113 * object when the <code>activateController</code> method
114 * is called. This default should be set by subclasses
115 * that choose to provide their own default controller,
116 * usually a GUI, for setting parameters.
117 *
118 * @see IIOParamController
119 * @see #getDefaultController
120 * @see #activateController
121 */
122 protected IIOParamController defaultController = null;
123
124 /**
125 * The <code>IIOParamController</code> that will be
126 * used to provide settings for this <code>IIOParam</code>
127 * object when the <code>activateController</code> method
128 * is called. This value overrides any default controller,
129 * even when null.
130 *
131 * @see IIOParamController
132 * @see #setController(IIOParamController)
133 * @see #hasController()
134 * @see #activateController()
135 */
136 protected IIOParamController controller = null;
137
138 /**
139 * Protected constructor may be called only by subclasses.
140 */
141 protected IIOParam() {
142 controller = defaultController;
143 }
144
145 /**
146 * Sets the source region of interest. The region of interest is
147 * described as a rectangle, with the upper-left corner of the
148 * source image as pixel (0, 0) and increasing values down and to
149 * the right. The actual number of pixels used will depend on
150 * the subsampling factors set by <code>setSourceSubsampling</code>.
151 * If subsampling has been set such that this number is zero,
152 * an <code>IllegalStateException</code> will be thrown.
153 *
154 * <p> The source region of interest specified by this method will
155 * be clipped as needed to fit within the source bounds, as well
156 * as the destination offsets, width, and height at the time of
157 * actual I/O.
158 *
159 * <p> A value of <code>null</code> for <code>sourceRegion</code>
160 * will remove any region specification, causing the entire image
161 * to be used.
162 *
163 * @param sourceRegion a <code>Rectangle</code> specifying the
164 * source region of interest, or <code>null</code>.
165 *
166 * @exception IllegalArgumentException if
167 * <code>sourceRegion</code> is non-<code>null</code> and either
168 * <code>sourceRegion.x</code> or <code>sourceRegion.y</code> is
169 * negative.
170 * @exception IllegalArgumentException if
171 * <code>sourceRegion</code> is non-<code>null</code> and either
172 * <code>sourceRegion.width</code> or
173 * <code>sourceRegion.height</code> is negative or 0.
174 * @exception IllegalStateException if subsampling is such that
175 * this region will have a subsampled width or height of zero.
176 *
177 * @see #getSourceRegion
178 * @see #setSourceSubsampling
179 * @see ImageReadParam#setDestinationOffset
180 * @see ImageReadParam#getDestinationOffset
181 */
182 public void setSourceRegion(Rectangle sourceRegion) {
183 if (sourceRegion == null) {
184 this .sourceRegion = null;
185 return;
186 }
187
188 if (sourceRegion.x < 0) {
189 throw new IllegalArgumentException("sourceRegion.x < 0!");
190 }
191 if (sourceRegion.y < 0) {
192 throw new IllegalArgumentException("sourceRegion.y < 0!");
193 }
194 if (sourceRegion.width <= 0) {
195 throw new IllegalArgumentException(
196 "sourceRegion.width <= 0!");
197 }
198 if (sourceRegion.height <= 0) {
199 throw new IllegalArgumentException(
200 "sourceRegion.height <= 0!");
201 }
202
203 // Throw an IllegalStateException if region falls between subsamples
204 if (sourceRegion.width <= subsamplingXOffset) {
205 throw new IllegalStateException(
206 "sourceRegion.width <= subsamplingXOffset!");
207 }
208 if (sourceRegion.height <= subsamplingYOffset) {
209 throw new IllegalStateException(
210 "sourceRegion.height <= subsamplingYOffset!");
211 }
212
213 this .sourceRegion = (Rectangle) sourceRegion.clone();
214 }
215
216 /**
217 * Returns the source region to be used. The returned value is
218 * that set by the most recent call to
219 * <code>setSourceRegion</code>, and will be <code>null</code> if
220 * there is no region set.
221 *
222 * @return the source region of interest as a
223 * <code>Rectangle</code>, or <code>null</code>.
224 *
225 * @see #setSourceRegion
226 */
227 public Rectangle getSourceRegion() {
228 if (sourceRegion == null) {
229 return null;
230 }
231 return (Rectangle) sourceRegion.clone();
232 }
233
234 /**
235 * Specifies a decimation subsampling to apply on I/O. The
236 * <code>sourceXSubsampling</code> and
237 * <code>sourceYSubsampling</code> parameters specify the
238 * subsampling period (<i>i.e.</i>, the number of rows and columns
239 * to advance after every source pixel). Specifically, a period of
240 * 1 will use every row or column; a period of 2 will use every
241 * other row or column. The <code>subsamplingXOffset</code> and
242 * <code>subsamplingYOffset</code> parameters specify an offset
243 * from the region (or image) origin for the first subsampled pixel.
244 * Adjusting the origin of the subsample grid is useful for avoiding
245 * seams when subsampling a very large source image into destination
246 * regions that will be assembled into a complete subsampled image.
247 * Most users will want to simply leave these parameters at 0.
248 *
249 * <p> The number of pixels and scanlines to be used are calculated
250 * as follows.
251 * <p>
252 * The number of subsampled pixels in a scanline is given by
253 * <p>
254 * <code>truncate[(width - subsamplingXOffset + sourceXSubsampling - 1)
255 * / sourceXSubsampling]</code>.
256 * <p>
257 * If the region is such that this width is zero, an
258 * <code>IllegalStateException</code> is thrown.
259 * <p>
260 * The number of scanlines to be used can be computed similarly.
261 *
262 * <p>The ability to set the subsampling grid to start somewhere
263 * other than the source region origin is useful if the
264 * region is being used to create subsampled tiles of a large image,
265 * where the tile width and height are not multiples of the
266 * subsampling periods. If the subsampling grid does not remain
267 * consistent from tile to tile, there will be artifacts at the tile
268 * boundaries. By adjusting the subsampling grid offset for each
269 * tile to compensate, these artifacts can be avoided. The tradeoff
270 * is that in order to avoid these artifacts, the tiles are not all
271 * the same size. The grid offset to use in this case is given by:
272 * <br>
273 * grid offset = [period - (region offset modulo period)] modulo period)
274 *
275 * <p> If either <code>sourceXSubsampling</code> or
276 * <code>sourceYSubsampling</code> is 0 or negative, an
277 * <code>IllegalArgumentException</code> will be thrown.
278 *
279 * <p> If either <code>subsamplingXOffset</code> or
280 * <code>subsamplingYOffset</code> is negative or greater than or
281 * equal to the corresponding period, an
282 * <code>IllegalArgumentException</code> will be thrown.
283 *
284 * <p> There is no <code>unsetSourceSubsampling</code> method;
285 * simply call <code>setSourceSubsampling(1, 1, 0, 0)</code> to
286 * restore default values.
287 *
288 * @param sourceXSubsampling the number of columns to advance
289 * between pixels.
290 * @param sourceYSubsampling the number of rows to advance between
291 * pixels.
292 * @param subsamplingXOffset the horizontal offset of the first subsample
293 * within the region, or within the image if no region is set.
294 * @param subsamplingYOffset the horizontal offset of the first subsample
295 * within the region, or within the image if no region is set.
296 * @exception IllegalArgumentException if either period is
297 * negative or 0, or if either grid offset is negative or greater than
298 * the corresponding period.
299 * @exception IllegalStateException if the source region is such that
300 * the subsampled output would contain no pixels.
301 */
302 public void setSourceSubsampling(int sourceXSubsampling,
303 int sourceYSubsampling, int subsamplingXOffset,
304 int subsamplingYOffset) {
305 if (sourceXSubsampling <= 0) {
306 throw new IllegalArgumentException(
307 "sourceXSubsampling <= 0!");
308 }
309 if (sourceYSubsampling <= 0) {
310 throw new IllegalArgumentException(
311 "sourceYSubsampling <= 0!");
312 }
313 if (subsamplingXOffset < 0
314 || subsamplingXOffset >= sourceXSubsampling) {
315 throw new IllegalArgumentException(
316 "subsamplingXOffset out of range!");
317 }
318 if (subsamplingYOffset < 0
319 || subsamplingYOffset >= sourceYSubsampling) {
320 throw new IllegalArgumentException(
321 "subsamplingYOffset out of range!");
322 }
323
324 // Throw an IllegalStateException if region falls between subsamples
325 if (sourceRegion != null) {
326 if (subsamplingXOffset >= sourceRegion.width
327 || subsamplingYOffset >= sourceRegion.height) {
328 throw new IllegalStateException(
329 "region contains no pixels!");
330 }
331 }
332
333 this .sourceXSubsampling = sourceXSubsampling;
334 this .sourceYSubsampling = sourceYSubsampling;
335 this .subsamplingXOffset = subsamplingXOffset;
336 this .subsamplingYOffset = subsamplingYOffset;
337 }
338
339 /**
340 * Returns the number of source columns to advance for each pixel.
341 *
342 * <p>If <code>setSourceSubsampling</code> has not been called, 1
343 * is returned (which is the correct value).
344 *
345 * @return the source subsampling X period.
346 *
347 * @see #setSourceSubsampling
348 * @see #getSourceYSubsampling
349 */
350 public int getSourceXSubsampling() {
351 return sourceXSubsampling;
352 }
353
354 /**
355 * Returns the number of rows to advance for each pixel.
356 *
357 * <p>If <code>setSourceSubsampling</code> has not been called, 1
358 * is returned (which is the correct value).
359 *
360 * @return the source subsampling Y period.
361 *
362 * @see #setSourceSubsampling
363 * @see #getSourceXSubsampling
364 */
365 public int getSourceYSubsampling() {
366 return sourceYSubsampling;
367 }
368
369 /**
370 * Returns the horizontal offset of the subsampling grid.
371 *
372 * <p>If <code>setSourceSubsampling</code> has not been called, 0
373 * is returned (which is the correct value).
374 *
375 * @return the source subsampling grid X offset.
376 *
377 * @see #setSourceSubsampling
378 * @see #getSubsamplingYOffset
379 */
380 public int getSubsamplingXOffset() {
381 return subsamplingXOffset;
382 }
383
384 /**
385 * Returns the vertical offset of the subsampling grid.
386 *
387 * <p>If <code>setSourceSubsampling</code> has not been called, 0
388 * is returned (which is the correct value).
389 *
390 * @return the source subsampling grid Y offset.
391 *
392 * @see #setSourceSubsampling
393 * @see #getSubsamplingXOffset
394 */
395 public int getSubsamplingYOffset() {
396 return subsamplingYOffset;
397 }
398
399 /**
400 * Sets the indices of the source bands to be used. Duplicate
401 * indices are not allowed.
402 *
403 * <p> A <code>null</code> value indicates that all source bands
404 * will be used.
405 *
406 * <p> At the time of reading, an
407 * <code>IllegalArgumentException</code> will be thrown by the
408 * reader or writer if a value larger than the largest available
409 * source band index has been specified or if the number of source
410 * bands and destination bands to be used differ. The
411 * <code>ImageReader.checkReadParamBandSettings</code> method may
412 * be used to automate this test.
413 *
414 * <p> Semantically, a copy is made of the array; changes to the
415 * array contents subsequent to this call have no effect on
416 * this <code>IIOParam</code>.
417 *
418 * @param sourceBands an array of integer band indices to be
419 * used.
420 *
421 * @exception IllegalArgumentException if <code>sourceBands</code>
422 * contains a negative or duplicate value.
423 *
424 * @see #getSourceBands
425 * @see ImageReadParam#setDestinationBands
426 * @see ImageReader#checkReadParamBandSettings
427 */
428 public void setSourceBands(int[] sourceBands) {
429 if (sourceBands == null) {
430 this .sourceBands = null;
431 } else {
432 int numBands = sourceBands.length;
433 for (int i = 0; i < numBands; i++) {
434 int band = sourceBands[i];
435 if (band < 0) {
436 throw new IllegalArgumentException(
437 "Band value < 0!");
438 }
439 for (int j = i + 1; j < numBands; j++) {
440 if (band == sourceBands[j]) {
441 throw new IllegalArgumentException(
442 "Duplicate band value!");
443 }
444 }
445
446 }
447 this .sourceBands = (int[]) (sourceBands.clone());
448 }
449 }
450
451 /**
452 * Returns the set of of source bands to be used. The returned
453 * value is that set by the most recent call to
454 * <code>setSourceBands</code>, or <code>null</code> if there have
455 * been no calls to <code>setSourceBands</code>.
456 *
457 * <p> Semantically, the array returned is a copy; changes to
458 * array contents subsequent to this call have no effect on this
459 * <code>IIOParam</code>.
460 *
461 * @return the set of source bands to be used, or
462 * <code>null</code>.
463 *
464 * @see #setSourceBands
465 */
466 public int[] getSourceBands() {
467 if (sourceBands == null) {
468 return null;
469 }
470 return (int[]) (sourceBands.clone());
471 }
472
473 /**
474 * Sets the desired image type for the destination image, using an
475 * <code>ImageTypeSpecifier</code>.
476 *
477 * <p> When reading, if the layout of the destination has been set
478 * using this method, each call to an <code>ImageReader</code>
479 * <code>read</code> method will return a new
480 * <code>BufferedImage</code> using the format specified by the
481 * supplied type specifier. As a side effect, any destination
482 * <code>BufferedImage</code> set by
483 * <code>ImageReadParam.setDestination(BufferedImage)</code> will
484 * no longer be set as the destination. In other words, this
485 * method may be thought of as calling
486 * <code>setDestination((BufferedImage)null)</code>.
487 *
488 * <p> When writing, the destination type maybe used to determine
489 * the color type of the image. The <code>SampleModel</code>
490 * information will be ignored, and may be <code>null</code>. For
491 * example, a 4-banded image could represent either CMYK or RGBA
492 * data. If a destination type is set, its
493 * <code>ColorModel</code> will override any
494 * <code>ColorModel</code> on the image itself. This is crucial
495 * when <code>setSourceBands</code> is used since the image's
496 * <code>ColorModel</code> will refer to the entire image rather
497 * than to the subset of bands being written.
498 *
499 * @param destinationType the <code>ImageTypeSpecifier</code> to
500 * be used to determine the destination layout and color type.
501 *
502 * @see #getDestinationType
503 */
504 public void setDestinationType(ImageTypeSpecifier destinationType) {
505 this .destinationType = destinationType;
506 }
507
508 /**
509 * Returns the type of image to be returned by the read, if one
510 * was set by a call to
511 * <code>setDestination(ImageTypeSpecifier)</code>, as an
512 * <code>ImageTypeSpecifier</code>. If none was set,
513 * <code>null</code> is returned.
514 *
515 * @return an <code>ImageTypeSpecifier</code> describing the
516 * destination type, or <code>null</code>.
517 *
518 * @see #setDestinationType
519 */
520 public ImageTypeSpecifier getDestinationType() {
521 return destinationType;
522 }
523
524 /**
525 * Specifies the offset in the destination image at which future
526 * decoded pixels are to be placed, when reading, or where a
527 * region will be written, when writing.
528 *
529 * <p> When reading, the region to be written within the
530 * destination <code>BufferedImage</code> will start at this
531 * offset and have a width and height determined by the source
532 * region of interest, the subsampling parameters, and the
533 * destination bounds.
534 *
535 * <p> Normal writes are not affected by this method, only writes
536 * performed using <code>ImageWriter.replacePixels</code>. For
537 * such writes, the offset specified is within the output stream
538 * image whose pixels are being modified.
539 *
540 * <p> There is no <code>unsetDestinationOffset</code> method;
541 * simply call <code>setDestinationOffset(new Point(0, 0))</code> to
542 * restore default values.
543 *
544 * @param destinationOffset the offset in the destination, as a
545 * <code>Point</code>.
546 *
547 * @exception IllegalArgumentException if
548 * <code>destinationOffset</code> is <code>null</code>.
549 *
550 * @see #getDestinationOffset
551 * @see ImageWriter#replacePixels
552 */
553 public void setDestinationOffset(Point destinationOffset) {
554 if (destinationOffset == null) {
555 throw new IllegalArgumentException(
556 "destinationOffset == null!");
557 }
558 this .destinationOffset = (Point) destinationOffset.clone();
559 }
560
561 /**
562 * Returns the offset in the destination image at which pixels are
563 * to be placed.
564 *
565 * <p> If <code>setDestinationOffsets</code> has not been called,
566 * a <code>Point</code> with zero X and Y values is returned
567 * (which is the correct value).
568 *
569 * @return the destination offset as a <code>Point</code>.
570 *
571 * @see #setDestinationOffset
572 */
573 public Point getDestinationOffset() {
574 return (Point) destinationOffset.clone();
575 }
576
577 /**
578 * Sets the <code>IIOParamController</code> to be used
579 * to provide settings for this <code>IIOParam</code>
580 * object when the <code>activateController</code> method
581 * is called, overriding any default controller. If the
582 * argument is <code>null</code>, no controller will be
583 * used, including any default. To restore the default, use
584 * <code>setController(getDefaultController())</code>.
585 *
586 * @param controller An appropriate
587 * <code>IIOParamController</code>, or <code>null</code>.
588 *
589 * @see IIOParamController
590 * @see #getController
591 * @see #getDefaultController
592 * @see #hasController
593 * @see #activateController()
594 */
595 public void setController(IIOParamController controller) {
596 this .controller = controller;
597 }
598
599 /**
600 * Returns whatever <code>IIOParamController</code> is currently
601 * installed. This could be the default if there is one,
602 * <code>null</code>, or the argument of the most recent call
603 * to <code>setController</code>.
604 *
605 * @return the currently installed
606 * <code>IIOParamController</code>, or <code>null</code>.
607 *
608 * @see IIOParamController
609 * @see #setController
610 * @see #getDefaultController
611 * @see #hasController
612 * @see #activateController()
613 */
614 public IIOParamController getController() {
615 return controller;
616 }
617
618 /**
619 * Returns the default <code>IIOParamController</code>, if there
620 * is one, regardless of the currently installed controller. If
621 * there is no default controller, returns <code>null</code>.
622 *
623 * @return the default <code>IIOParamController</code>, or
624 * <code>null</code>.
625 *
626 * @see IIOParamController
627 * @see #setController(IIOParamController)
628 * @see #getController
629 * @see #hasController
630 * @see #activateController()
631 */
632 public IIOParamController getDefaultController() {
633 return defaultController;
634 }
635
636 /**
637 * Returns <code>true</code> if there is a controller installed
638 * for this <code>IIOParam</code> object. This will return
639 * <code>true</code> if <code>getController</code> would not
640 * return <code>null</code>.
641 *
642 * @return <code>true</code> if a controller is installed.
643 *
644 * @see IIOParamController
645 * @see #setController(IIOParamController)
646 * @see #getController
647 * @see #getDefaultController
648 * @see #activateController()
649 */
650 public boolean hasController() {
651 return (controller != null);
652 }
653
654 /**
655 * Activates the installed <code>IIOParamController</code> for
656 * this <code>IIOParam</code> object and returns the resulting
657 * value. When this method returns <code>true</code>, all values
658 * for this <code>IIOParam</code> object will be ready for the
659 * next read or write operation. If <code>false</code> is
660 * returned, no settings in this object will have been disturbed
661 * (<i>i.e.</i>, the user canceled the operation).
662 *
663 * <p> Ordinarily, the controller will be a GUI providing a user
664 * interface for a subclass of <code>IIOParam</code> for a
665 * particular plug-in. Controllers need not be GUIs, however.
666 *
667 * @return <code>true</code> if the controller completed normally.
668 *
669 * @exception IllegalStateException if there is no controller
670 * currently installed.
671 *
672 * @see IIOParamController
673 * @see #setController(IIOParamController)
674 * @see #getController
675 * @see #getDefaultController
676 * @see #hasController
677 */
678 public boolean activateController() {
679 if (!hasController()) {
680 throw new IllegalStateException("hasController() == false!");
681 }
682 return getController().activate(this);
683 }
684 }
|