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