Ignore:
Timestamp:
2017-10-30T22:46:09+01:00 (8 years ago)
Author:
Don-vip
Message:

fix #15505 - update to metadata-extractor 2.10.1

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/com/drew/metadata/exif/ExifDescriptorBase.java

    r10862 r13061  
    11/*
    2  * Copyright 2002-2016 Drew Noakes
     2 * Copyright 2002-2017 Drew Noakes
    33 *
    44 *    Licensed under the Apache License, Version 2.0 (the "License");
     
    2626import com.drew.lang.annotations.NotNull;
    2727import com.drew.lang.annotations.Nullable;
     28import com.drew.lang.ByteArrayReader;
    2829import com.drew.metadata.Directory;
    2930import com.drew.metadata.TagDescriptor;
    3031
     32import java.io.IOException;
    3133import java.io.UnsupportedEncodingException;
    32 import java.math.RoundingMode;
    3334import java.text.DecimalFormat;
    3435import java.util.HashMap;
     
    4243 * @author Drew Noakes https://drewnoakes.com
    4344 */
     45@SuppressWarnings("WeakerAccess")
    4446public abstract class ExifDescriptorBase<T extends Directory> extends TagDescriptor<T>
    4547{
     
    4951     */
    5052    private final boolean _allowDecimalRepresentationOfRationals = true;
    51 
    52     @NotNull
    53     private static final java.text.DecimalFormat SimpleDecimalFormatter = new DecimalFormat("0.#");
    5453
    5554    // Note for the potential addition of brightness presentation in eV:
     
    123122            case TAG_FILL_ORDER:
    124123                return getFillOrderDescription();
     124            case TAG_CFA_PATTERN_2:
     125                return getCfaPattern2Description();
    125126            case TAG_EXPOSURE_TIME:
    126127                return getExposureTimeDescription();
     
    167168            case TAG_SCENE_TYPE:
    168169                return getSceneTypeDescription();
     170            case TAG_CFA_PATTERN:
     171                return getCfaPatternDescription();
    169172            case TAG_COMPONENTS_CONFIGURATION:
    170173                return getComponentConfigurationDescription();
     
    234237    public String getReferenceBlackWhiteDescription()
    235238    {
     239        // For some reason, sometimes this is read as a long[] and
     240        // getIntArray isn't able to deal with it
    236241        int[] ints = _directory.getIntArray(TAG_REFERENCE_BLACK_WHITE);
    237242        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
    239259        int blackR = ints[0];
    240260        int whiteR = ints[1];
     
    279299    public String getOrientationDescription()
    280300    {
    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);
    290302    }
    291303
     
    443455    public String getNewSubfileTypeDescription()
    444456    {
    445         return getIndexedDescription(TAG_NEW_SUBFILE_TYPE, 1,
     457        return getIndexedDescription(TAG_NEW_SUBFILE_TYPE, 0,
    446458            "Full-resolution image",
    447459            "Reduced-resolution image",
     460            "Single page of multi-page image",
    448461            "Single page of multi-page reduced-resolution image",
    449462            "Transparency mask",
     
    587600            : value.getNumerator() == 0
    588601                ? "Digital zoom not used"
    589                 : SimpleDecimalFormatter.format(value.doubleValue());
     602                : new DecimalFormat("0.#").format(value.doubleValue());
    590603    }
    591604
     
    689702            "Directly photographed image"
    690703        );
     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;
    691860    }
    692861
     
    10021171    public String getShutterSpeedDescription()
    10031172    {
    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);
    10481174    }
    10491175
Note: See TracChangeset for help on using the changeset viewer.