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} and 35 * {@code ImageWriteParam}. 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} 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}. 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}. 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}s indicating which source bands 88 * will be used, or {@code null}. If {@code null}, the 89 * set of source bands to be used is as described in the comment 90 * for the {@code setSourceBands} method. No value should 91 * be allowed to be negative. 92 */ 93 protected int[] sourceBands = null; 94 95 /** 96 * An {@code ImageTypeSpecifier} 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}. By default, the value is {@code null}. 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} that will be 111 * used to provide settings for this {@code IIOParam} 112 * object when the {@code activateController} 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} that will be 125 * used to provide settings for this {@code IIOParam} 126 * object when the {@code activateController} 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 */ IIOParam()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}. 150 * If subsampling has been set such that this number is zero, 151 * an {@code IllegalStateException} 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} for {@code sourceRegion} 159 * will remove any region specification, causing the entire image 160 * to be used. 161 * 162 * @param sourceRegion a {@code Rectangle} specifying the 163 * source region of interest, or {@code null}. 164 * 165 * @exception IllegalArgumentException if 166 * {@code sourceRegion} is non-{@code null} and either 167 * {@code sourceRegion.x} or {@code sourceRegion.y} is 168 * negative. 169 * @exception IllegalArgumentException if 170 * {@code sourceRegion} is non-{@code null} and either 171 * {@code sourceRegion.width} or 172 * {@code sourceRegion.height} 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 */ setSourceRegion(Rectangle sourceRegion)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}, and will be {@code null} if 217 * there is no region set. 218 * 219 * @return the source region of interest as a 220 * {@code Rectangle}, or {@code null}. 221 * 222 * @see #setSourceRegion 223 */ getSourceRegion()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} and 234 * {@code sourceYSubsampling} 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} and 239 * {@code subsamplingYOffset} 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]}. 253 * <p> 254 * If the region is such that this width is zero, an 255 * {@code IllegalStateException} 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} or 273 * {@code sourceYSubsampling} is 0 or negative, an 274 * {@code IllegalArgumentException} will be thrown. 275 * 276 * <p> If either {@code subsamplingXOffset} or 277 * {@code subsamplingYOffset} is negative or greater than or 278 * equal to the corresponding period, an 279 * {@code IllegalArgumentException} will be thrown. 280 * 281 * <p> There is no {@code unsetSourceSubsampling} method; 282 * simply call {@code setSourceSubsampling(1, 1, 0, 0)} 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 */ setSourceSubsampling(int sourceXSubsampling, int sourceYSubsampling, int subsamplingXOffset, int subsamplingYOffset)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} 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 */ getSourceXSubsampling()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} 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 */ getSourceYSubsampling()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} 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 */ getSubsamplingXOffset()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} 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 */ getSubsamplingYOffset()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} value indicates that all source bands 399 * will be used. 400 * 401 * <p> At the time of reading, an 402 * {@code IllegalArgumentException} 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} 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}. 412 * 413 * @param sourceBands an array of integer band indices to be 414 * used. 415 * 416 * @exception IllegalArgumentException if {@code sourceBands} 417 * contains a negative or duplicate value. 418 * 419 * @see #getSourceBands 420 * @see ImageReadParam#setDestinationBands 421 * @see ImageReader#checkReadParamBandSettings 422 */ setSourceBands(int[] sourceBands)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 source bands to be used. The returned 446 * value is that set by the most recent call to 447 * {@code setSourceBands}, or {@code null} if there have 448 * been no calls to {@code setSourceBands}. 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}. 453 * 454 * @return the set of source bands to be used, or 455 * {@code null}. 456 * 457 * @see #setSourceBands 458 */ getSourceBands()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}. 469 * 470 * <p> When reading, if the layout of the destination has been set 471 * using this method, each call to an {@code ImageReader} 472 * {@code read} method will return a new 473 * {@code BufferedImage} using the format specified by the 474 * supplied type specifier. As a side effect, any destination 475 * {@code BufferedImage} set by 476 * {@code ImageReadParam.setDestination(BufferedImage)} 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)}. 480 * 481 * <p> When writing, the destination type maybe used to determine 482 * the color type of the image. The {@code SampleModel} 483 * information will be ignored, and may be {@code null}. 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} will override any 487 * {@code ColorModel} on the image itself. This is crucial 488 * when {@code setSourceBands} is used since the image's 489 * {@code ColorModel} will refer to the entire image rather 490 * than to the subset of bands being written. 491 * 492 * @param destinationType the {@code ImageTypeSpecifier} to 493 * be used to determine the destination layout and color type. 494 * 495 * @see #getDestinationType 496 */ setDestinationType(ImageTypeSpecifier destinationType)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)}, as an 505 * {@code ImageTypeSpecifier}. If none was set, 506 * {@code null} is returned. 507 * 508 * @return an {@code ImageTypeSpecifier} describing the 509 * destination type, or {@code null}. 510 * 511 * @see #setDestinationType 512 */ getDestinationType()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} 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}. 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} method; 534 * simply call {@code setDestinationOffset(new Point(0, 0))} to 535 * restore default values. 536 * 537 * @param destinationOffset the offset in the destination, as a 538 * {@code Point}. 539 * 540 * @exception IllegalArgumentException if 541 * {@code destinationOffset} is {@code null}. 542 * 543 * @see #getDestinationOffset 544 * @see ImageWriter#replacePixels 545 */ setDestinationOffset(Point destinationOffset)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} has not been called, 558 * a {@code Point} with zero X and Y values is returned 559 * (which is the correct value). 560 * 561 * @return the destination offset as a {@code Point}. 562 * 563 * @see #setDestinationOffset 564 */ getDestinationOffset()565 public Point getDestinationOffset() { 566 return (Point)destinationOffset.clone(); 567 } 568 569 /** 570 * Sets the {@code IIOParamController} to be used 571 * to provide settings for this {@code IIOParam} 572 * object when the {@code activateController} method 573 * is called, overriding any default controller. If the 574 * argument is {@code null}, no controller will be 575 * used, including any default. To restore the default, use 576 * {@code setController(getDefaultController())}. 577 * 578 * @param controller An appropriate 579 * {@code IIOParamController}, or {@code null}. 580 * 581 * @see IIOParamController 582 * @see #getController 583 * @see #getDefaultController 584 * @see #hasController 585 * @see #activateController() 586 */ setController(IIOParamController controller)587 public void setController(IIOParamController controller) { 588 this.controller = controller; 589 } 590 591 /** 592 * Returns whatever {@code IIOParamController} is currently 593 * installed. This could be the default if there is one, 594 * {@code null}, or the argument of the most recent call 595 * to {@code setController}. 596 * 597 * @return the currently installed 598 * {@code IIOParamController}, or {@code null}. 599 * 600 * @see IIOParamController 601 * @see #setController 602 * @see #getDefaultController 603 * @see #hasController 604 * @see #activateController() 605 */ getController()606 public IIOParamController getController() { 607 return controller; 608 } 609 610 /** 611 * Returns the default {@code IIOParamController}, if there 612 * is one, regardless of the currently installed controller. If 613 * there is no default controller, returns {@code null}. 614 * 615 * @return the default {@code IIOParamController}, or 616 * {@code null}. 617 * 618 * @see IIOParamController 619 * @see #setController(IIOParamController) 620 * @see #getController 621 * @see #hasController 622 * @see #activateController() 623 */ getDefaultController()624 public IIOParamController getDefaultController() { 625 return defaultController; 626 } 627 628 /** 629 * Returns {@code true} if there is a controller installed 630 * for this {@code IIOParam} object. This will return 631 * {@code true} if {@code getController} would not 632 * return {@code null}. 633 * 634 * @return {@code true} if a controller is installed. 635 * 636 * @see IIOParamController 637 * @see #setController(IIOParamController) 638 * @see #getController 639 * @see #getDefaultController 640 * @see #activateController() 641 */ hasController()642 public boolean hasController() { 643 return (controller != null); 644 } 645 646 /** 647 * Activates the installed {@code IIOParamController} for 648 * this {@code IIOParam} object and returns the resulting 649 * value. When this method returns {@code true}, all values 650 * for this {@code IIOParam} object will be ready for the 651 * next read or write operation. If {@code false} 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} for a 657 * particular plug-in. Controllers need not be GUIs, however. 658 * 659 * @return {@code true} 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 */ activateController()670 public boolean activateController() { 671 if (!hasController()) { 672 throw new IllegalStateException("hasController() == false!"); 673 } 674 return getController().activate(this); 675 } 676 } 677