Changeset 13061 in josm for trunk/src/com/drew/metadata/exif/ExifDescriptorBase.java
- Timestamp:
- 2017-10-30T22:46:09+01:00 (8 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/com/drew/metadata/exif/ExifDescriptorBase.java
r10862 r13061 1 1 /* 2 * Copyright 2002-201 6Drew Noakes2 * Copyright 2002-2017 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 26 26 import com.drew.lang.annotations.NotNull; 27 27 import com.drew.lang.annotations.Nullable; 28 import com.drew.lang.ByteArrayReader; 28 29 import com.drew.metadata.Directory; 29 30 import com.drew.metadata.TagDescriptor; 30 31 32 import java.io.IOException; 31 33 import java.io.UnsupportedEncodingException; 32 import java.math.RoundingMode;33 34 import java.text.DecimalFormat; 34 35 import java.util.HashMap; … … 42 43 * @author Drew Noakes https://drewnoakes.com 43 44 */ 45 @SuppressWarnings("WeakerAccess") 44 46 public abstract class ExifDescriptorBase<T extends Directory> extends TagDescriptor<T> 45 47 { … … 49 51 */ 50 52 private final boolean _allowDecimalRepresentationOfRationals = true; 51 52 @NotNull53 private static final java.text.DecimalFormat SimpleDecimalFormatter = new DecimalFormat("0.#");54 53 55 54 // Note for the potential addition of brightness presentation in eV: … … 123 122 case TAG_FILL_ORDER: 124 123 return getFillOrderDescription(); 124 case TAG_CFA_PATTERN_2: 125 return getCfaPattern2Description(); 125 126 case TAG_EXPOSURE_TIME: 126 127 return getExposureTimeDescription(); … … 167 168 case TAG_SCENE_TYPE: 168 169 return getSceneTypeDescription(); 170 case TAG_CFA_PATTERN: 171 return getCfaPatternDescription(); 169 172 case TAG_COMPONENTS_CONFIGURATION: 170 173 return getComponentConfigurationDescription(); … … 234 237 public String getReferenceBlackWhiteDescription() 235 238 { 239 // For some reason, sometimes this is read as a long[] and 240 // getIntArray isn't able to deal with it 236 241 int[] ints = _directory.getIntArray(TAG_REFERENCE_BLACK_WHITE); 237 242 if (ints==null || ints.length < 6) 238 return null; 243 { 244 Object o = _directory.getObject(TAG_REFERENCE_BLACK_WHITE); 245 if (o != null && (o instanceof long[])) 246 { 247 long[] longs = (long[])o; 248 if (longs.length < 6) 249 return null; 250 251 ints = new int[longs.length]; 252 for (int i = 0; i < longs.length; i++) 253 ints[i] = (int)longs[i]; 254 } 255 else 256 return null; 257 } 258 239 259 int blackR = ints[0]; 240 260 int whiteR = ints[1]; … … 279 299 public String getOrientationDescription() 280 300 { 281 return getIndexedDescription(TAG_ORIENTATION, 1, 282 "Top, left side (Horizontal / normal)", 283 "Top, right side (Mirror horizontal)", 284 "Bottom, right side (Rotate 180)", 285 "Bottom, left side (Mirror vertical)", 286 "Left side, top (Mirror horizontal and rotate 270 CW)", 287 "Right side, top (Rotate 90 CW)", 288 "Right side, bottom (Mirror horizontal and rotate 90 CW)", 289 "Left side, bottom (Rotate 270 CW)"); 301 return super.getOrientationDescription(TAG_ORIENTATION); 290 302 } 291 303 … … 443 455 public String getNewSubfileTypeDescription() 444 456 { 445 return getIndexedDescription(TAG_NEW_SUBFILE_TYPE, 1,457 return getIndexedDescription(TAG_NEW_SUBFILE_TYPE, 0, 446 458 "Full-resolution image", 447 459 "Reduced-resolution image", 460 "Single page of multi-page image", 448 461 "Single page of multi-page reduced-resolution image", 449 462 "Transparency mask", … … 587 600 : value.getNumerator() == 0 588 601 ? "Digital zoom not used" 589 : SimpleDecimalFormatter.format(value.doubleValue());602 : new DecimalFormat("0.#").format(value.doubleValue()); 590 603 } 591 604 … … 689 702 "Directly photographed image" 690 703 ); 704 } 705 706 /// <summary> 707 /// String description of CFA Pattern 708 /// </summary> 709 /// <remarks> 710 /// Converted from Exiftool version 10.33 created by Phil Harvey 711 /// http://www.sno.phy.queensu.ca/~phil/exiftool/ 712 /// lib\Image\ExifTool\Exif.pm 713 /// 714 /// Indicates the color filter array (CFA) geometric pattern of the image sensor when a one-chip color area sensor is used. 715 /// It does not apply to all sensing methods. 716 /// </remarks> 717 @Nullable 718 public String getCfaPatternDescription() 719 { 720 return formatCFAPattern(decodeCfaPattern(TAG_CFA_PATTERN)); 721 } 722 723 /// <summary> 724 /// String description of CFA Pattern 725 /// </summary> 726 /// <remarks> 727 /// Indicates the color filter array (CFA) geometric pattern of the image sensor when a one-chip color area sensor is used. 728 /// It does not apply to all sensing methods. 729 /// 730 /// ExifDirectoryBase.TAG_CFA_PATTERN_2 holds only the pixel pattern. ExifDirectoryBase.TAG_CFA_REPEAT_PATTERN_DIM is expected to exist and pass 731 /// some conditional tests. 732 /// </remarks> 733 @Nullable 734 public String getCfaPattern2Description() 735 { 736 byte[] values = _directory.getByteArray(TAG_CFA_PATTERN_2); 737 if (values == null) 738 return null; 739 740 int[] repeatPattern = _directory.getIntArray(TAG_CFA_REPEAT_PATTERN_DIM); 741 if (repeatPattern == null) 742 return String.format("Repeat Pattern not found for CFAPattern (%s)", super.getDescription(TAG_CFA_PATTERN_2)); 743 744 if (repeatPattern.length == 2 && values.length == (repeatPattern[0] * repeatPattern[1])) 745 { 746 int[] intpattern = new int[2 + values.length]; 747 intpattern[0] = repeatPattern[0]; 748 intpattern[1] = repeatPattern[1]; 749 750 for (int i = 0; i < values.length; i++) 751 intpattern[i + 2] = values[i] & 0xFF; // convert the values[i] byte to unsigned 752 753 return formatCFAPattern(intpattern); 754 } 755 756 return String.format("Unknown Pattern (%s)", super.getDescription(TAG_CFA_PATTERN_2)); 757 } 758 759 @Nullable 760 private static String formatCFAPattern(@Nullable int[] pattern) 761 { 762 if (pattern == null) 763 return null; 764 if (pattern.length < 2) 765 return "<truncated data>"; 766 if (pattern[0] == 0 && pattern[1] == 0) 767 return "<zero pattern size>"; 768 769 int end = 2 + pattern[0] * pattern[1]; 770 if (end > pattern.length) 771 return "<invalid pattern size>"; 772 773 String[] cfaColors = { "Red", "Green", "Blue", "Cyan", "Magenta", "Yellow", "White" }; 774 775 StringBuilder ret = new StringBuilder(); 776 ret.append("["); 777 for (int pos = 2; pos < end; pos++) 778 { 779 if (pattern[pos] <= cfaColors.length - 1) 780 ret.append(cfaColors[pattern[pos]]); 781 else 782 ret.append("Unknown"); // indicated pattern position is outside the array bounds 783 784 if ((pos - 2) % pattern[1] == 0) 785 ret.append(","); 786 else if(pos != end - 1) 787 ret.append("]["); 788 } 789 ret.append("]"); 790 791 return ret.toString(); 792 } 793 794 /// <summary> 795 /// Decode raw CFAPattern value 796 /// </summary> 797 /// <remarks> 798 /// Converted from Exiftool version 10.33 created by Phil Harvey 799 /// http://www.sno.phy.queensu.ca/~phil/exiftool/ 800 /// lib\Image\ExifTool\Exif.pm 801 /// 802 /// The value consists of: 803 /// - Two short, being the grid width and height of the repeated pattern. 804 /// - Next, for every pixel in that pattern, an identification code. 805 /// </remarks> 806 @Nullable 807 private int[] decodeCfaPattern(int tagType) 808 { 809 int[] ret; 810 811 byte[] values = _directory.getByteArray(tagType); 812 if (values == null) 813 return null; 814 815 if (values.length < 4) 816 { 817 ret = new int[values.length]; 818 for (int i = 0; i < values.length; i++) 819 ret[i] = values[i]; 820 return ret; 821 } 822 823 ret = new int[values.length - 2]; 824 825 try { 826 ByteArrayReader reader = new ByteArrayReader(values); 827 828 // first two values should be read as 16-bits (2 bytes) 829 short item0 = reader.getInt16(0); 830 short item1 = reader.getInt16(2); 831 832 Boolean copyArray = false; 833 int end = 2 + item0 * item1; 834 if (end > values.length) // sanity check in case of byte order problems; calculated 'end' should be <= length of the values 835 { 836 // try swapping byte order (I have seen this order different than in EXIF) 837 reader.setMotorolaByteOrder(!reader.isMotorolaByteOrder()); 838 item0 = reader.getInt16(0); 839 item1 = reader.getInt16(2); 840 841 if (values.length >= (2 + item0 * item1)) 842 copyArray = true; 843 } 844 else 845 copyArray = true; 846 847 if(copyArray) 848 { 849 ret[0] = item0; 850 ret[1] = item1; 851 852 for (int i = 4; i < values.length; i++) 853 ret[i - 2] = reader.getInt8(i); 854 } 855 } catch (IOException ex) { 856 _directory.addError("IO exception processing data: " + ex.getMessage()); 857 } 858 859 return ret; 691 860 } 692 861 … … 1002 1171 public String getShutterSpeedDescription() 1003 1172 { 1004 // I believe this method to now be stable, but am leaving some alternative snippets of 1005 // code in here, to assist anyone who's looking into this (given that I don't have a public CVS). 1006 1007 // float apexValue = _directory.getFloat(ExifSubIFDDirectory.TAG_SHUTTER_SPEED); 1008 // int apexPower = (int)Math.pow(2.0, apexValue); 1009 // return "1/" + apexPower + " sec"; 1010 // TODO test this method 1011 // thanks to Mark Edwards for spotting and patching a bug in the calculation of this 1012 // description (spotted bug using a Canon EOS 300D) 1013 // thanks also to Gli Blr for spotting this bug 1014 Float apexValue = _directory.getFloatObject(TAG_SHUTTER_SPEED); 1015 if (apexValue == null) 1016 return null; 1017 if (apexValue <= 1) { 1018 float apexPower = (float)(1 / (Math.exp(apexValue * Math.log(2)))); 1019 long apexPower10 = Math.round((double)apexPower * 10.0); 1020 float fApexPower = (float)apexPower10 / 10.0f; 1021 DecimalFormat format = new DecimalFormat("0.##"); 1022 format.setRoundingMode(RoundingMode.HALF_UP); 1023 return format.format(fApexPower) + " sec"; 1024 } else { 1025 int apexPower = (int)((Math.exp(apexValue * Math.log(2)))); 1026 return "1/" + apexPower + " sec"; 1027 } 1028 1029 /* 1030 // This alternative implementation offered by Bill Richards 1031 // TODO determine which is the correct / more-correct implementation 1032 double apexValue = _directory.getDouble(ExifSubIFDDirectory.TAG_SHUTTER_SPEED); 1033 double apexPower = Math.pow(2.0, apexValue); 1034 1035 StringBuffer sb = new StringBuffer(); 1036 if (apexPower > 1) 1037 apexPower = Math.floor(apexPower); 1038 1039 if (apexPower < 1) { 1040 sb.append((int)Math.round(1/apexPower)); 1041 } else { 1042 sb.append("1/"); 1043 sb.append((int)apexPower); 1044 } 1045 sb.append(" sec"); 1046 return sb.toString(); 1047 */ 1173 return super.getShutterSpeedDescription(TAG_SHUTTER_SPEED); 1048 1174 } 1049 1175
Note:
See TracChangeset
for help on using the changeset viewer.
