001 /* 002 * TIFFImageFileDirectory 003 * 004 * Copyright (c) 2001, 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.codecs.tiff; 009 010 import java.text.ParseException; 011 import java.text.SimpleDateFormat; 012 import java.util.Date; 013 import java.util.GregorianCalendar; 014 import java.util.TimeZone; 015 import java.util.Vector; 016 import net.sourceforge.jiu.codecs.InvalidFileStructureException; 017 import net.sourceforge.jiu.codecs.UnsupportedTypeException; 018 import net.sourceforge.jiu.codecs.tiff.TIFFConstants; 019 import net.sourceforge.jiu.codecs.tiff.TIFFTag; 020 import net.sourceforge.jiu.data.Palette; 021 import net.sourceforge.jiu.data.RGBIndex; 022 023 /** 024 * This class encapsulates all data of a TIFF image file directory (IFD). 025 * @author Marco Schmidt 026 */ 027 public class TIFFImageFileDirectory implements TIFFConstants 028 { 029 public static final int TYPE_BILEVEL_PACKED = 0; 030 public static final int TYPE_GRAY4 = 1; 031 public static final int TYPE_GRAY8 = 2; 032 public static final int TYPE_GRAY16 = 3; 033 public static final int TYPE_PALETTED4 = 4; 034 public static final int TYPE_PALETTED8 = 5; 035 public static final int TYPE_RGB24_INTERLEAVED = 6; 036 public static final int TYPE_RGB48_INTERLEAVED = 7; 037 public static final int TYPE_BILEVEL_BYTE = 8; 038 public static final int TYPE_CMYK32_INTERLEAVED = 9; 039 public static final int TYPE_CMYK32_PLANAR = 10; 040 public static final int TYPE_LOGLUV32_INTERLEAVED = 11; 041 public static final int TYPE_LOGL = 12; 042 043 private String artist; 044 private int[] bitsPerSample; 045 private int bitsPerPixel; 046 private int bitsPerRow; 047 private int bytesBetweenSamples; 048 private int[] bytesPerSample; 049 private int bytesPerRow; 050 private int compression; 051 private String copyright; 052 private Date date; 053 private String dateTime; 054 private int dpiX; 055 private int dpiY; 056 private int[] extraSamples; 057 private int height; 058 private int horizontalTiles; 059 private String hostComputer; 060 private String imageDescription; 061 private int imageType; 062 private boolean invertGraySamples; 063 private String make; 064 private String model; 065 private int numStrips; 066 private int numTiles; 067 private int orientation; 068 private Palette palette; 069 private int pixelsPerRow; 070 private int planarConfiguration; 071 private int photometricInterpretation; 072 private int predictor; 073 private int[] sampleTypes; 074 private int resolutionUnit; 075 private double resolutionX; 076 private double resolutionY; 077 private int rowsPerStrip; 078 private int samplesPerPixel; 079 private String software; 080 private Vector stripByteCounts; 081 private Vector stripOffsets; 082 private int t4Options; 083 private int t6Options; 084 private Vector tags; 085 private Vector tileByteCounts; 086 private Vector tileOffsets; 087 private TimeZone timeZone; 088 private int tileWidth; 089 private int tileHeight; 090 private int totalTiles; 091 private int verticalTiles; 092 private int width; 093 094 /** 095 * Initializes all members to null or -1 and creates an internal list for 096 * the tags that will be make up this directory. 097 */ 098 public TIFFImageFileDirectory() 099 { 100 initMembers(); 101 tags = new Vector(); 102 } 103 104 /** 105 * Adds a tag to the end of the internal list of tags. 106 * @param tag the TIFFTag instance to be appended 107 */ 108 public void append(TIFFTag tag) 109 { 110 tags.addElement(tag); 111 } 112 113 private void checkContent() throws 114 InvalidFileStructureException, 115 UnsupportedTypeException 116 { 117 if (width < 1) 118 { 119 throw new InvalidFileStructureException("No valid width available."); 120 } 121 if (height < 1) 122 { 123 throw new InvalidFileStructureException("No valid width available."); 124 } 125 if (stripOffsets != null) 126 { 127 pixelsPerRow = width; 128 } 129 else 130 if (tileOffsets != null) 131 { 132 pixelsPerRow = tileWidth; 133 } 134 if (rowsPerStrip == -1 && stripOffsets != null && stripOffsets.size() == 1) 135 { 136 rowsPerStrip = height; 137 } 138 // do more checks based on color type 139 switch (photometricInterpretation) 140 { 141 case(PHOTOMETRIC_BLACK_IS_ZERO): 142 case(PHOTOMETRIC_WHITE_IS_ZERO): 143 { 144 if (bitsPerSample[0] == 1) 145 { 146 imageType = TYPE_BILEVEL_PACKED; 147 } 148 else 149 { 150 if (bitsPerSample[0] == 4) 151 { 152 imageType = TYPE_GRAY4; 153 } 154 else 155 if (bitsPerSample[0] == 8) 156 { 157 imageType = TYPE_GRAY8; 158 } 159 else 160 { 161 throw new UnsupportedTypeException("Only bit depths 1, 4 and 8 are supported for bilevel and grayscale images."); 162 } 163 } 164 break; 165 } 166 case(PHOTOMETRIC_PALETTED): 167 { 168 if (getPalette() == null) 169 { 170 throw new InvalidFileStructureException("No palette found in paletted image."); 171 } 172 break; 173 } 174 case(PHOTOMETRIC_TRUECOLOR_RGB): 175 { 176 if (planarConfiguration != PLANAR_CONFIGURATION_CHUNKY) 177 { 178 throw new UnsupportedTypeException("Cannot handle planar configuration other than chunky for RGB images."); 179 } 180 if (bitsPerSample.length != 3) 181 { 182 throw new UnsupportedTypeException("Found RGB truecolor image, but instead of three " + bitsPerSample.length + " component(s)."); 183 } 184 if (bitsPerPixel == 24) 185 { 186 imageType = TYPE_RGB24_INTERLEAVED; 187 } 188 else 189 if (bitsPerPixel == 48) 190 { 191 imageType = TYPE_RGB48_INTERLEAVED; 192 } 193 else 194 { 195 throw new UnsupportedTypeException("Unsupported RGB truecolor image color depth: " + bitsPerPixel + "."); 196 } 197 break; 198 } 199 case(PHOTOMETRIC_TRUECOLOR_LOGLUV): 200 { 201 if (planarConfiguration == PLANAR_CONFIGURATION_CHUNKY) 202 { 203 imageType = TYPE_LOGLUV32_INTERLEAVED; 204 } 205 else 206 { 207 throw new UnsupportedTypeException("Cannot handle planar configuration other than chunky for RGB images."); 208 } 209 break; 210 } 211 case(PHOTOMETRIC_LOGL): 212 { 213 imageType = TYPE_LOGL; 214 break; 215 } 216 case(PHOTOMETRIC_TRUECOLOR_CMYK): 217 { 218 if (planarConfiguration == PLANAR_CONFIGURATION_CHUNKY) 219 { 220 imageType = TYPE_CMYK32_INTERLEAVED; 221 } 222 /*else 223 if (planarConfiguration == PLANAR_CONFIGURATION_PLANAR) 224 { 225 imageType = TYPE_CMYK32_PLANAR; 226 }*/ 227 else 228 { 229 throw new UnsupportedTypeException("Cannot handle planar configuration other than chunky for CMYK images."); 230 } 231 break; 232 } 233 default: 234 { 235 throw new UnsupportedTypeException("Unsupported color type: " + photometricInterpretation + "."); 236 } 237 } 238 if (compression == COMPRESSION_CCITT_GROUP3_1D_MODIFIED_HUFFMAN) 239 { 240 if (bitsPerPixel != 1) 241 { 242 throw new UnsupportedTypeException("Number of bits per pixel must be 1 for " + 243 "compression type: " + getCompressionName(compression) + "."); 244 } 245 imageType = TYPE_BILEVEL_BYTE; 246 } 247 // TODO more validity checks 248 } 249 250 /** 251 * TODO: regard extra samples 252 */ 253 public int computeNumBytes(int numPixels) 254 { 255 if (bitsPerPixel == 1) 256 { 257 if (imageType == TYPE_BILEVEL_BYTE) 258 { 259 return numPixels; 260 } 261 else 262 { 263 return (numPixels + 7) / 8; 264 } 265 } 266 else 267 if (bitsPerPixel <= 4) 268 { 269 return (numPixels + 1) / 2; 270 } 271 else 272 if (bitsPerPixel <= 8) 273 { 274 return numPixels; 275 } 276 else 277 if (bitsPerPixel == 16) 278 { 279 return numPixels * 2; 280 } 281 else 282 if (bitsPerPixel == 24) 283 { 284 return numPixels * 3; 285 } 286 else 287 if (bitsPerPixel == 32) 288 { 289 return numPixels * 4; 290 } 291 else 292 if (bitsPerPixel == 48) 293 { 294 return numPixels * 6; 295 } 296 else 297 { 298 return -1; 299 } 300 } 301 302 /** 303 * Returns information on the person who created the image 304 * (as stored in tag {@link TIFFConstants#TAG_ARTIST}). 305 */ 306 public String getArtist() 307 { 308 return artist; 309 } 310 311 /** 312 * Returns the number of bits per pixel (not including transparency information). 313 */ 314 public int getBitsPerPixel() 315 { 316 return bitsPerPixel; 317 } 318 319 /** 320 * Returns the number of compressed byte for a given tile. 321 * Tile index must not be negative and must be smaller than the number of tiles. 322 * @param tileIndex zero-based index of tile or strip for which the number of compressed bytes is to be returned 323 */ 324 public int getByteCount(int tileIndex) 325 { 326 if (stripByteCounts != null) 327 { 328 return ((Number)stripByteCounts.elementAt(tileIndex)).intValue(); 329 } 330 else 331 if (tileByteCounts != null) 332 { 333 return ((Number)tileByteCounts.elementAt(tileIndex)).intValue(); 334 } 335 else 336 { 337 return 0; 338 } 339 } 340 341 public int getBytesPerRow() 342 { 343 return computeNumBytes(getTileWidth()); 344 } 345 346 /** 347 * Returns the compression method, encoded as a number as found in 348 * {@link TIFFConstants} (more specifically, the COMPRESSION_xyz constants). 349 * Use {@link #getCompressionName(int)} to get the English name 350 * of this compression method. 351 * @return compression method 352 */ 353 public int getCompression() 354 { 355 return compression; 356 } 357 358 /** 359 * Returns the name of a TIFF compression method. 360 * If the name is unknown, <em>Unknown method</em> plus 361 * the method number is returned. 362 * This static method can be used in combination with the value from 363 * {@link #getCompression}. 364 * @param method the compression method number 365 * @return the compression method name 366 */ 367 public static String getCompressionName(int method) 368 { 369 switch(method) 370 { 371 case(COMPRESSION_CCITT_GROUP3_1D_MODIFIED_HUFFMAN): return "CCITT Group 3 1D Modified Huffman"; 372 case(COMPRESSION_CCITT_T4): return "CCITT T.4"; 373 case(COMPRESSION_CCITT_T6): return "CCITT T.6"; 374 case(COMPRESSION_DEFLATED_INOFFICIAL): return "Deflated (inofficial, 32496)"; 375 case(COMPRESSION_DEFLATED_OFFICIAL): return "Deflated (official, 8)"; 376 case(COMPRESSION_LZW): return "LZW"; 377 case(COMPRESSION_NONE): return "Uncompressed"; 378 case(COMPRESSION_PACKBITS): return "Packbits"; 379 case(6): return "JPEG (old style)"; 380 case(7):return "JPEG (new style)"; 381 case(103): return "Pegasus IMJ"; 382 case(32766): return "NeXT 2-bit RLE"; 383 case(32771): return "Uncompressed, word-aligned"; 384 case(32809): return "Thunderscan RLE"; 385 case(32895): return "IT8 CT with padding"; 386 case(32896): return "IT8 Linework RLE"; 387 case(32897): return "IT8 Monochrome picture"; 388 case(32898): return "IT8 Binary line art"; 389 case(32908): return "Pixar 10 bit LZW"; 390 case(32909): return "Pixar 11 bit ZIP"; 391 case(32947): return "Kodak DCS"; 392 case(34661): return "ISO JBIG"; 393 case(34676): return "SGI Log Luminance RLE"; 394 case(34677): return "SGI Log 24-bit packed"; 395 default: return "Unknown method (" + method + ")"; 396 } 397 } 398 399 public String getCopyright() 400 { 401 return copyright; 402 } 403 404 /** 405 * If a date / time tag was found in this image file directory and 406 * {@link #initFromTags} was called already, it was attempted to 407 * create a {@link java.util.Date} object from it. 408 * This object (or <code>null</code>) is returned. 409 * Use {@link #setTimeZone} to provide a time zone before the date 410 * parsing is done. 411 * @see #getDateTimeString 412 */ 413 public Date getDateTime() 414 { 415 return date; 416 } 417 418 /** 419 * If there was a date / time tag in this IFD, its String value 420 * is returned. 421 * @see #getDateTime 422 */ 423 public String getDateTimeString() 424 { 425 return dateTime; 426 } 427 428 public int getDpiX() 429 { 430 return dpiX; 431 } 432 433 public int getDpiY() 434 { 435 return dpiY; 436 } 437 438 public int getHeight() 439 { 440 return height; 441 } 442 443 public String getHostComputer() 444 { 445 return hostComputer; 446 } 447 448 public String getImageDescription() 449 { 450 return imageDescription; 451 } 452 453 public int getImageType() 454 { 455 return imageType; 456 } 457 458 public String getModel() 459 { 460 return model; 461 } 462 463 public int getNumHorizontalTiles() 464 { 465 return horizontalTiles; 466 } 467 468 public int getNumStrips() 469 { 470 return numStrips; 471 } 472 473 public int getNumTiles() 474 { 475 return numTiles; 476 } 477 478 public int getNumVerticalTiles() 479 { 480 return verticalTiles; 481 } 482 483 public Palette getPalette() 484 { 485 return palette; 486 } 487 488 public int getPhotometricInterpretation() 489 { 490 return photometricInterpretation; 491 } 492 493 public int getPredictor() 494 { 495 return predictor; 496 } 497 498 public int getRowsPerStrip() 499 { 500 return rowsPerStrip; 501 } 502 503 public int getSamplesPerPixel() 504 { 505 return samplesPerPixel; 506 } 507 508 public String getSoftware() 509 { 510 return software; 511 } 512 513 public Vector getStripOffsets() 514 { 515 return stripOffsets; 516 } 517 518 public int getT4Options() 519 { 520 return t4Options; 521 } 522 523 public int getT6Options() 524 { 525 return t6Options; 526 } 527 528 public int getTileHeight() 529 { 530 return tileHeight; 531 } 532 533 public long getTileOffset(int tileIndex) 534 { 535 if (stripOffsets != null) 536 { 537 Number number = (Number)stripOffsets.elementAt(tileIndex); 538 return number.longValue(); 539 } 540 else 541 if (tileOffsets != null) 542 { 543 Number number = (Number)tileOffsets.elementAt(tileIndex); 544 return number.longValue(); 545 } 546 else 547 { 548 throw new IllegalArgumentException("Tile index invalid: " + tileIndex); 549 } 550 } 551 552 public int getTileWidth() 553 { 554 return tileWidth; 555 } 556 557 public int getTileX1(int tileIndex) 558 { 559 if (tileIndex < 0 || tileIndex >= getNumTiles()) 560 { 561 throw new IllegalArgumentException("Not a valid tile index: " + tileIndex); 562 } 563 else 564 { 565 return (tileIndex % getNumHorizontalTiles()) * getTileWidth(); 566 } 567 } 568 569 public int getTileX2(int tileIndex) 570 { 571 if (tileIndex < 0 || tileIndex >= getNumTiles()) 572 { 573 throw new IllegalArgumentException("Not a valid tile index: " + tileIndex); 574 } 575 else 576 { 577 return ((tileIndex % getNumHorizontalTiles()) + 1) * getTileWidth() - 1; 578 } 579 } 580 581 public int getTileY1(int tileIndex) 582 { 583 if (tileIndex < 0 || tileIndex >= getNumTiles()) 584 { 585 throw new IllegalArgumentException("Not a valid tile index: " + tileIndex); 586 } 587 else 588 { 589 return (tileIndex % getNumVerticalTiles()) * getTileHeight(); 590 } 591 } 592 593 public int getTileY2(int tileIndex) 594 { 595 if (tileIndex < 0 || tileIndex >= getNumTiles()) 596 { 597 throw new IllegalArgumentException("Not a valid tile index: " + tileIndex); 598 } 599 else 600 { 601 int result = ((tileIndex % getNumVerticalTiles()) + 1) * getTileHeight() - 1; 602 if (result >= height) 603 { 604 result = height - 1; 605 } 606 return result; 607 } 608 } 609 610 public int getWidth() 611 { 612 return width; 613 } 614 615 public void initMembers() 616 { 617 bitsPerPixel = -1; 618 bitsPerSample = null; 619 compression = -1; 620 height = -1; 621 horizontalTiles = -1; 622 invertGraySamples = false; 623 numStrips = -1; 624 numTiles = -1; 625 orientation = 1; 626 photometricInterpretation = -1; 627 planarConfiguration = -1; 628 resolutionUnit = 2; 629 resolutionX = -1.0; 630 resolutionY = -1.0; 631 rowsPerStrip = -1; 632 stripOffsets = null; 633 tags = null; 634 tileOffsets = null; 635 tileWidth = -1; 636 tileHeight = -1; 637 totalTiles = -1; 638 verticalTiles = -1; 639 width = -1; 640 } 641 642 public void initFromTags(boolean check) throws 643 InvalidFileStructureException, 644 UnsupportedTypeException 645 { 646 int index = 0; 647 while (index < tags.size()) 648 { 649 TIFFTag tag = (TIFFTag)tags.elementAt(index++); 650 int id = tag.getId(); 651 int count = tag.getCount(); 652 int type = tag.getType(); 653 boolean isNotInt = !tag.isInt(); 654 switch(id) 655 { 656 case(TAG_ARTIST): 657 { 658 artist = tag.getString(); 659 break; 660 } 661 case(TAG_BITS_PER_SAMPLE): 662 { 663 if (isNotInt) 664 { 665 throw new InvalidFileStructureException("Bits per " + 666 "sample value(s) must be byte/short/long; type=" + 667 type); 668 } 669 if (count == 1) 670 { 671 bitsPerSample = new int[1]; 672 bitsPerSample[0] = tag.getOffset(); 673 bitsPerPixel = bitsPerSample[0]; 674 } 675 else 676 { 677 bitsPerPixel = 0; 678 bitsPerSample = new int[count]; 679 for (int i = 0; i < count; i++) 680 { 681 bitsPerSample[i] = tag.getElementAsInt(i); 682 if (bitsPerSample[i] < 1) 683 { 684 throw new InvalidFileStructureException("Bits per " + 685 "sample value #" + i + " is smaller than 1."); 686 } 687 bitsPerPixel += bitsPerSample[i]; 688 } 689 } 690 break; 691 } 692 case(TAG_COLOR_MAP): 693 { 694 if ((count % 3) != 0) 695 { 696 throw new InvalidFileStructureException("Number of palette entries must be divideable by three without rest; " + count); 697 } 698 if (count < 3 || count > 768) 699 { 700 throw new UnsupportedTypeException("Unsupported number of palette entries: " + count + "."); 701 } 702 if (type != TAG_TYPE_SHORT) 703 { 704 throw new UnsupportedTypeException("Unsupported number type for palette entries: " + type); 705 } 706 int numEntries = count / 3; 707 palette = new Palette(numEntries, 255); 708 int vectorIndex = 0; 709 for (int paletteIndex = 0; paletteIndex < numEntries; paletteIndex++) 710 { 711 palette.putSample(RGBIndex.INDEX_RED, paletteIndex, tag.getElementAsInt(vectorIndex++) >> 8); 712 } 713 for (int paletteIndex = 0; paletteIndex < numEntries; paletteIndex++) 714 { 715 palette.putSample(RGBIndex.INDEX_GREEN, paletteIndex, tag.getElementAsInt(vectorIndex++) >> 8); 716 } 717 for (int paletteIndex = 0; paletteIndex < numEntries; paletteIndex++) 718 { 719 palette.putSample(RGBIndex.INDEX_BLUE, paletteIndex, tag.getElementAsInt(vectorIndex++) >> 8); 720 } 721 break; 722 } 723 case(TAG_COMPRESSION): 724 { 725 if (count != 1 || isNotInt) 726 { 727 throw new InvalidFileStructureException("Expected " + 728 " single byte/short/long value for compression " + 729 "(count=" + count + ", type=" + type + ")."); 730 } 731 compression = tag.getOffset(); 732 break; 733 } 734 case(TAG_DATE_TIME): 735 { 736 dateTime = tag.getString(); 737 if (dateTime != null) 738 { 739 dateTime = dateTime.trim(); 740 } 741 if (dateTime != null) 742 { 743 SimpleDateFormat format = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); 744 if (timeZone != null) 745 { 746 format.setCalendar(new GregorianCalendar(timeZone)); 747 } 748 try 749 { 750 date = format.parse(dateTime); 751 } 752 catch (ParseException pe) 753 { 754 date = null; 755 } 756 } 757 break; 758 } 759 case(TAG_HOST_COMPUTER): 760 { 761 hostComputer = tag.getString(); 762 break; 763 } 764 case(TAG_IMAGE_DESCRIPTION): 765 { 766 imageDescription = tag.getString(); 767 break; 768 } 769 case(TAG_IMAGE_WIDTH): 770 { 771 if (count != 1 || isNotInt) 772 { 773 throw new InvalidFileStructureException("Expected " + 774 "single byte/short/long value for image width " + 775 "(count=" + count + ", type=" + type + ")."); 776 } 777 width = tag.getOffset(); 778 break; 779 } 780 case(TAG_IMAGE_LENGTH): 781 { 782 if (count != 1 || isNotInt) 783 { 784 throw new InvalidFileStructureException("Expected " + 785 "single byte/short/long value for image height " + 786 "(count=" + count + ", type=" + type + ")."); 787 } 788 height = tag.getOffset(); 789 break; 790 } 791 case(TAG_MAKE): 792 { 793 make = tag.getString(); 794 break; 795 } 796 case(TAG_MODEL): 797 { 798 model = tag.getString(); 799 break; 800 } 801 case(TAG_ORIENTATION): 802 { 803 if (count != 1 || isNotInt) 804 { 805 throw new InvalidFileStructureException("Expected " + 806 "single byte/short/long value for image height " + 807 "(count=" + count + ", type=" + type + ")."); 808 } 809 orientation = tag.getOffset(); 810 break; 811 } 812 case(TAG_PHOTOMETRIC_INTERPRETATION): 813 { 814 if (count != 1 || isNotInt) 815 { 816 throw new InvalidFileStructureException("Expected " + 817 "single byte/short/long value for photometric interpretation."); 818 } 819 photometricInterpretation = tag.getOffset(); 820 break; 821 } 822 case(TAG_RESOLUTION_UNIT): 823 { 824 if (count != 1 || isNotInt) 825 { 826 throw new InvalidFileStructureException("Expected " + 827 "single byte/short/long value for planar configuration."); 828 } 829 resolutionUnit = tag.getOffset(); 830 break; 831 } 832 case(TAG_RESOLUTION_X): 833 { 834 if (count != 1 || type != TAG_TYPE_RATIONAL) 835 { 836 throw new InvalidFileStructureException("Expected " + 837 "single byte/short/long value for planar configuration."); 838 } 839 Object o = tag.getObject(0); 840 if (o != null && o instanceof TIFFRational) 841 { 842 TIFFRational rational = (TIFFRational)o; 843 resolutionX = rational.getAsDouble(); 844 } 845 break; 846 } 847 case(TAG_RESOLUTION_Y): 848 { 849 if (count != 1 || type != TAG_TYPE_RATIONAL) 850 { 851 throw new InvalidFileStructureException("Expected " + 852 "single byte/short/long value for planar configuration."); 853 } 854 Object o = tag.getObject(0); 855 if (o != null && o instanceof TIFFRational) 856 { 857 TIFFRational rational = (TIFFRational)o; 858 resolutionY = rational.getAsDouble(); 859 } 860 break; 861 } 862 case(TAG_PLANAR_CONFIGURATION): 863 { 864 if (count != 1 || isNotInt) 865 { 866 throw new InvalidFileStructureException("Expected " + 867 "single byte/short/long value for planar configuration."); 868 } 869 planarConfiguration = tag.getOffset(); 870 break; 871 } 872 case(TAG_ROWS_PER_STRIP): 873 { 874 if (count != 1 || isNotInt) 875 { 876 throw new InvalidFileStructureException("Expected " + 877 "single byte/short/long value for image height."); 878 } 879 rowsPerStrip = tag.getOffset(); 880 break; 881 } 882 case(TAG_SAMPLES_PER_PIXEL): 883 { 884 if (count != 1 || isNotInt) 885 { 886 throw new InvalidFileStructureException("Expected " + 887 "single byte/short/long value for samples per pixel."); 888 } 889 samplesPerPixel = tag.getOffset(); 890 break; 891 } 892 case(TAG_SOFTWARE): 893 { 894 software = tag.getString(); 895 break; 896 } 897 case(TAG_STRIP_BYTE_COUNTS): 898 { 899 if (count < 1) 900 { 901 throw new InvalidFileStructureException("Need at least one strip offset."); 902 } 903 if (count == 1) 904 { 905 if (isNotInt) 906 { 907 throw new InvalidFileStructureException("There is " + 908 "only one strip offset, but its type is not integer."); 909 } 910 stripByteCounts = new Vector(); 911 stripByteCounts.addElement(new Long(tag.getOffset())); 912 } 913 else 914 { 915 stripByteCounts = tag.getVector(); 916 } 917 break; 918 } 919 case(TAG_STRIP_OFFSETS): 920 { 921 if (count < 1) 922 { 923 throw new InvalidFileStructureException("Need at least one strip offset."); 924 } 925 if (count == 1) 926 { 927 if (isNotInt) 928 { 929 throw new InvalidFileStructureException("There is " + 930 "only one strip offset, but its type is not integer."); 931 } 932 stripOffsets = new Vector(); 933 stripOffsets.addElement(new Long(tag.getOffset())); 934 } 935 else 936 { 937 stripOffsets = tag.getVector(); 938 } 939 numStrips = count; 940 numTiles = count; 941 horizontalTiles = 1; 942 verticalTiles = count; 943 //println("strip offsets: " + stripOffsets.size()); 944 break; 945 } 946 case(TAG_T4_OPTIONS): 947 { 948 if (count != 1 || isNotInt) 949 { 950 throw new InvalidFileStructureException("Expected " + 951 "single byte/short/long value for T4 Options."); 952 } 953 t4Options = tag.getOffset(); 954 break; 955 } 956 case(TAG_T6_OPTIONS): 957 { 958 if (count != 1 || isNotInt) 959 { 960 throw new InvalidFileStructureException("Expected " + 961 "single byte/short/long value for T6 Options."); 962 } 963 t6Options = tag.getOffset(); 964 break; 965 } 966 case(TAG_TILE_HEIGHT): 967 { 968 if (count != 1 || isNotInt) 969 { 970 throw new InvalidFileStructureException("Expected " + 971 "single byte/short/long value for image height " + 972 "(count=" + count + ", type=" + type + ")."); 973 } 974 tileHeight = tag.getOffset(); 975 if (tileHeight < 1) 976 { 977 throw new InvalidFileStructureException("Tile height must be one or larger."); 978 } 979 verticalTiles = height / tileHeight; 980 if ((height % tileHeight) != 0) 981 { 982 verticalTiles++; 983 } 984 break; 985 } 986 case(TAG_TILE_OFFSETS): 987 { 988 if (count < 1) 989 { 990 throw new InvalidFileStructureException("Need at least one tile offset."); 991 } 992 if (count == 1) 993 { 994 if (isNotInt) 995 { 996 throw new InvalidFileStructureException("There is " + 997 "only one tile offset, but its type is not integer."); 998 } 999 tileOffsets = new Vector(); 1000 tileOffsets.addElement(new Long(tag.getOffset())); 1001 } 1002 else 1003 { 1004 tileOffsets = tag.getVector(); 1005 } 1006 numStrips = count; 1007 numTiles = count; 1008 horizontalTiles = 1; 1009 verticalTiles = count; 1010 //println("strip offsets: " + stripOffsets.size()); 1011 break; 1012 } 1013 case(TAG_TILE_WIDTH): 1014 { 1015 if (count != 1 || isNotInt) 1016 { 1017 throw new InvalidFileStructureException("Expected " + 1018 "single byte/short/long value for image height " + 1019 "(count=" + count + ", type=" + type + ")."); 1020 } 1021 tileWidth = tag.getOffset(); 1022 if (tileWidth < 1) 1023 { 1024 throw new InvalidFileStructureException("Tile width must be one or larger."); 1025 } 1026 horizontalTiles = width / tileWidth; 1027 if ((width % tileWidth) != 0) 1028 { 1029 horizontalTiles++; 1030 } 1031 break; 1032 } 1033 } 1034 } 1035 if (planarConfiguration == -1) 1036 { 1037 planarConfiguration = PLANAR_CONFIGURATION_CHUNKY; 1038 } 1039 if (photometricInterpretation == TIFFConstants.PHOTOMETRIC_PALETTED) 1040 { 1041 if (bitsPerPixel == 4) 1042 { 1043 imageType = TYPE_PALETTED4; 1044 } 1045 else 1046 if (bitsPerPixel == 8) 1047 { 1048 imageType = TYPE_PALETTED8; 1049 } 1050 else 1051 { 1052 throw new UnsupportedTypeException("Only paletted images with 4 or 8 bits per sample are supported."); 1053 } 1054 } 1055 if (resolutionUnit == 2 && resolutionX > 0.0 && resolutionY > 0.0) 1056 { 1057 dpiX = (int)resolutionX; 1058 dpiY = (int)resolutionY; 1059 } 1060 if (isStriped()) 1061 { 1062 tileWidth = width; 1063 if (numStrips == 1 && rowsPerStrip == -1) 1064 { 1065 rowsPerStrip = height; 1066 } 1067 tileHeight = rowsPerStrip; 1068 } 1069 if (check) 1070 { 1071 checkContent(); 1072 } 1073 } 1074 1075 public boolean isGrayscale() 1076 { 1077 return getBitsPerPixel() > 1 && 1078 (photometricInterpretation == PHOTOMETRIC_BLACK_IS_ZERO || 1079 photometricInterpretation == PHOTOMETRIC_WHITE_IS_ZERO); 1080 } 1081 1082 public boolean isPaletted() 1083 { 1084 return (photometricInterpretation == PHOTOMETRIC_PALETTED); 1085 } 1086 1087 /** 1088 * Returns <code>true</code> if the image belonging to this IFD 1089 * is stored as strips, <code>false</code> otherwise. 1090 * @see #isTiled 1091 */ 1092 public boolean isStriped() 1093 { 1094 return (stripOffsets != null); 1095 } 1096 1097 /** 1098 * Returns <code>true</code> if the image belonging to this IFD 1099 * is stored as tiles, <code>false</code> otherwise. 1100 * @see #isStriped 1101 */ 1102 public boolean isTiled() 1103 { 1104 return (tileOffsets != null); 1105 } 1106 1107 /** 1108 * Sets the time zone to be used when trying to interpret dates 1109 * found in a {@link #TAG_DATE_TIME} tag. 1110 * Example call: 1111 * <code>setTimeZone(TimeZone.getTimeZone("America/New_York");</code>. 1112 * @param tz TimeZone object 1113 */ 1114 public void setTimeZone(TimeZone tz) 1115 { 1116 timeZone = tz; 1117 } 1118 }