/** * ********************************************************************************************************************* * * @author Fabrizio Giudici * @version $Id$ * <p>******************************************************************************************************************** */ public class PEFDecoder { private static final String CLASS = PEFDecoder.class.getName(); private static final Logger logger = Logger.getLogger(CLASS); private int bitBuffer = 0; private int availableBitCount = 0; private boolean reset = false; private final int bitCountTable[] = new int[1 << 12]; private final byte byteTable[] = new byte[1 << 12]; public void load(final @Nonnull DataInputStream iis) throws IOException { final short bit0[] = new short[13]; final short bit1[] = new short[13]; for (int c = 0; c < 13; c++) { bit0[c] = iis.readShort(); } for (int c = 0; c < 13; c++) { bit1[c] = iis.readByte(); } for (int c = 0; c < 13; c++) { for (int i = bit0[c]; i <= ((bit0[c] + (bitCountTable.length >> bit1[c]) - 1) & (bitCountTable.length - 1)); ) { bitCountTable[i] = bit1[c]; byteTable[i] = (byte) c; i++; } } // logger.finest(">>>> bitCountTable: %s", Arrays.toString(bitCountTable)); // logger.finest(">>>> byteTable: %s", Arrays.toString(byteTable)); } public int decode( final @Nonnegative int bitCount, final boolean useTable, final @Nonnull RAWImageInputStream iis, final boolean zeroAfterFF) throws IOException { if ((bitCount == 0) || (availableBitCount < 0)) { return 0; } int value; // TODO: RAWImageInputStream supports zeroAfterFF - use it and simplify this loop // while (!reset && (availableBitCount < bitCount) && ((value = iis.read() & 0xff) != 0xff) && // // more correct, but breaks while (!reset && (availableBitCount < bitCount) && ((value = iis.read() & 0xff) != -1) && !(reset = zeroAfterFF && (value == 0xff) && (iis.read() != 0))) { bitBuffer = (bitBuffer << 8) | value; // (uchar)c availableBitCount += 8; } value = (bitBuffer << (32 - availableBitCount)) >>> (32 - bitCount); if (useTable) { availableBitCount -= bitCountTable[value]; value = byteTable[value] & 0xff; // (uchar)... } else { availableBitCount -= bitCount; } assert (availableBitCount >= 0); // logger.finest("getbithuff: returning c: %d, bitbuf: %x, vbits: %d", c, bitbuf, vbits); return value; } }
/** * ********************************************************************************************************************* * * @author Fabrizio Giudici * @version $Id$ * <p>******************************************************************************************************************** */ public abstract class RAWImageReaderSpiSupport extends ImageReaderSpi { private static final String CLASS = RAWImageReaderSpiSupport.class.getName(); private static final Logger logger = Logger.getLogger(CLASS); /** A postprocessor, if available, will be run against the loaded image. */ private static Map<Class<?>, PostProcessor> postProcessorMapBySpiClass = new HashMap<Class<?>, PostProcessor>(); /** * ***************************************************************************************************************** * * @param names * @param suffixes * @param MIMETypes * @param readerClass * @param inputTypes * @param writerSpiNames * @param supportsStandardStreamMetadataFormat * @param nativeStreamMetadataFormatName * @param nativeStreamMetadataFormatClassName * @param extraStreamMetadataFormatNames * @param extraStreamMetadataFormatClassNames * @param supportsStandardImageMetadataFormat * @param nativeImageMetadataFormatName * @param nativeImageMetadataFormatClassName * @param extraImageMetadataFormatNames * @param extraImageMetadataFormatClassNames * <p>**************************************************************************************************************** */ protected RAWImageReaderSpiSupport( String[] names, String[] suffixes, String[] MIMETypes, Class readerClass, Class[] inputTypes, String[] writerSpiNames, boolean supportsStandardStreamMetadataFormat, String nativeStreamMetadataFormatName, String nativeStreamMetadataFormatClassName, String[] extraStreamMetadataFormatNames, String[] extraStreamMetadataFormatClassNames, boolean supportsStandardImageMetadataFormat, String nativeImageMetadataFormatName, String nativeImageMetadataFormatClassName, String[] extraImageMetadataFormatNames, String[] extraImageMetadataFormatClassNames) { super( "tidalwave.it", Version.TAG, names, suffixes, MIMETypes, readerClass.getName(), inputTypes, writerSpiNames, supportsStandardStreamMetadataFormat, nativeStreamMetadataFormatName, nativeStreamMetadataFormatClassName, extraStreamMetadataFormatNames, extraStreamMetadataFormatClassNames, supportsStandardImageMetadataFormat, nativeImageMetadataFormatName, nativeImageMetadataFormatClassName, extraImageMetadataFormatNames, extraImageMetadataFormatClassNames); } /** * ***************************************************************************************************************** * * @param name * @param suffixes * @param mimeType * @param readerClass * <p>***************************************************************************** */ protected RAWImageReaderSpiSupport( String name, String[] suffixes, String mimeType, Class readerClass) { this( new String[] {name.toLowerCase(), name.toUpperCase()}, suffixes, new String[] {mimeType}, readerClass, new Class[] {ImageInputStream.class}, // inputTypes null, // writerSpiNames false, // supportsStandardStreamMetadataFormat null, // nativeStreamMetadataFormatName null, // nativeStreamMetadataFormatClassName null, // extraStreamMetadataFormatNames null, // extraStreamMetadataFormatClassNames false, // supportsStandardImageMetadataFormat null, // nativeImageMetadataFormatName null, // nativeImageMetadataFormatClassName null, // extraImageMetadataFormatNames null); // extraImageMetadataFormatClassNam } /** * ***************************************************************************************************************** * * @param name * @param suffix * @param mimeType * @param readerClass * <p>***************************************************************************** */ protected RAWImageReaderSpiSupport( String name, String suffix, String mimeType, Class readerClass) { this(name, new String[] {suffix.toLowerCase(), suffix.toUpperCase()}, mimeType, readerClass); } /** * ***************************************************************************************************************** * * <p>Installs a postprocessor that will be run against all the instances of images loaded by this * Spi. * * @param postProcessor the post processor to install * <p>***************************************************************************** */ public static void installPostProcessor(Class<?> spiClass, PostProcessor postProcessor) { postProcessorMapBySpiClass.put(spiClass, postProcessor); } /** * ***************************************************************************************************************** * * <p>Post-processes a raw image using the installed postprocessor, if any. * * @param image the raw image to postprocess * @return the post-processed image * <p>**************************************************************************************************************** */ @Nonnull protected BufferedImage postProcess( final @Nonnull BufferedImage image, final @Nonnull RAWMetadataSupport metadata, final @Nonnull RAWImageReadParam readParam) { logger.fine("postProcess(%s, %s, %s)", image, metadata.getClass(), readParam); final Source source = readParam.lookup(Source.class); final PostProcessor postProcessor = !source.needsPostProcessor() ? null : postProcessorMapBySpiClass.get(getClass()); logger.finer(">>>> source: %s, postProcessor: %s", source, postProcessor); return (postProcessor != null) ? postProcessor.process(image, metadata, readParam) : image; } /** * ***************************************************************************************************************** * * <p>Post-processes a raw image using the installed postprocessor, if any. * * @param image the raw image to postprocess * @return the post-processed image * <p>**************************************************************************************************************** */ protected void postProcessMetadata( final @Nonnull RAWMetadataSupport metadata, final @Nonnull RAWImageReadParam readParam) { logger.fine("postProcessMetadata(%s, %s)", metadata.getClass(), readParam); final Source source = readParam.lookup(Source.class); final PostProcessor postProcessor = !source.needsPostProcessor() ? null : postProcessorMapBySpiClass.get(getClass()); logger.finer(">>>> source: %s, postProcessor: %s", source, postProcessor); if (postProcessor != null) { postProcessor.processMetadata(metadata, readParam); } } /** * ***************************************************************************************************************** * * @inheritDoc * **************************************************************************************************************** */ public boolean canDecodeInput(Object source) throws IOException { if (source instanceof ImageInputStream) { return canDecodeInput((ImageInputStream) source); } else { ImageInputStream iis = null; try { iis = ImageIO.createImageInputStream(source); if (iis != null) { return canDecodeInput(iis); } } finally { if (iis != null) { iis.close(); } } } return false; } /** * ***************************************************************************************************************** * * <p>**************************************************************************************************************** */ private boolean canDecodeInput(ImageInputStream source) throws IOException { RAWImageInputStream iis = new RAWImageInputStreamImpl(source); iis .setDontCloseDelegate(); // otherwise the garbage collector will close it together with the // original source!! try { iis.mark(); return canDecodeInput(iis); } catch (Exception e) { return false; } finally { iis.setBaseOffset(0); iis.reset(); } } /** * ***************************************************************************************************************** * * @param iis * @return * @throws IOException * <p>**************************************************************************************************************** */ protected abstract boolean canDecodeInput(RAWImageInputStream iis) throws IOException; }
/** * ********************************************************************************************************************* * * @author Fabrizio Giudici * @version $Id$ * <p>******************************************************************************************************************** */ public class NEFImageReaderSpi extends RAWImageReaderSpiSupport { private static final String CLASS = NEFImageReaderSpi.class.getName(); private static final Logger logger = Logger.getLogger(CLASS); private static final List supportedModels = Arrays.asList(new String[] {"E5700", "E8700", "E5400", "E8800"}); /** * ***************************************************************************************************************** * * <p>***************************************************************************** */ public NEFImageReaderSpi() { super("NEF", new String[] {"nef", "ndf"}, "image/x-nikon-nef", NEFImageReader.class); } /** * ***************************************************************************************************************** * * <p>**************************************************************************************************************** */ @Nonnull public String getDescription(final Locale locale) { return "Standard NEF Image Reader"; } /** * ***************************************************************************************************************** * * <p>**************************************************************************************************************** */ @Nonnull public ImageReader createReaderInstance(@CheckForNull final Object extension) throws IOException { return new NEFImageReader(this, extension); } /** * ***************************************************************************************************************** * * @param iis * @return * @throws IOException * <p>***************************************************************************** */ protected boolean canDecodeInput(@Nonnull final RAWImageInputStream iis) throws IOException { iis.seek(0); NEFHeaderProcessor headerProcessor = new NEFHeaderProcessor(); headerProcessor.process(iis); long ifdOffset = TIFFImageReaderSupport.processHeader(iis, headerProcessor); if (ifdOffset == 20) // TODO: bad fix for NDF files, must test for NDF instead - headerProcessor.isNDF() { ifdOffset -= NEFHeaderProcessor.NDF_OFFSET; iis.setBaseOffset( NEFHeaderProcessor .NDF_OFFSET); // TODO: move this behaviour as generic, with hproc.getBaseOffset() } IFD primaryIFD = new IFD(); primaryIFD.load(iis, ifdOffset); if (primaryIFD.isDNGVersionAvailable()) { logger.finest(">>>> FAILING, isDNGVersionAvailable returning true"); return false; } String make = primaryIFD.getMake(); String model = primaryIFD.getModel(); logger.finest("Make: %s, Model: %s", make, model); // // Beware that TIFF files out of Nikon scanners are tagged as Nikon. // Check the model name too. // if ((make == null) || !make.toUpperCase().startsWith("NIKON") || (model == null) || (!model.toUpperCase().startsWith("NIKON D") && !supportedModels.contains(model.toUpperCase()))) { logger.finest(">>>> FAILING, supportedModels: %s", supportedModels); return false; } return true; } }
/** * ********************************************************************************************************************* * * @author Fabrizio Giudici * @version $Id$ * <p>******************************************************************************************************************** */ public abstract class Directory implements Serializable { private static final String CLASS = Directory.class.getName(); private static final Logger logger = Logger.getLogger(CLASS); private static final long serialVersionUID = 7068468438676854749L; protected long start; protected long end; /** The map of getTags. */ private final Map<Object, AbstractTag> tagMapByKey = new HashMap<Object, AbstractTag>(); /** The list of tag keys, in the same order as they were added. */ private final List<Object> keyList = new ArrayList<Object>(); protected Directory nextDirectory; // FIXME it's protected - better to make private /** The list of sub-directories. */ private final Collection<Directory> directoryList = new ArrayList<Directory>(); /** The map of named directories. */ private Map<String, Directory> directoryMapByName = new HashMap<String, Directory>(); /** The name of the registry this tag belongs to. */ private String registryName; /** * The registry the contained getTags belongs to. This is transient so that this class can be * serialized without carrying along the whole registry. Upon deserialization the link to the * registry must be restored by using the registryName. */ protected transient TagRegistry tagRegistry; /** * ***************************************************************************************************************** * * <p>This class models an enumerated value for a tag. * * <p>**************************************************************************************************************** */ public static class Enumeration implements Serializable { private static final long serialVersionUID = 4029468438676854749L; /** The tag value. */ private int intValue; private String stringValue; /** The tag name. */ private String description; protected Enumeration(int value, String description) { this.intValue = value; this.description = description; } protected Enumeration(int[] value, String description) { this(value[0], description); } protected Enumeration(String value, String description) { this.stringValue = value; this.description = description; } protected Enumeration(String[] value, String description) { this(value[0], description); } public int intValue() { return intValue; } @Override public String toString() { if (stringValue != null) { return description.equals("reserved") ? (description + "#" + stringValue) : description; } else { return description.equals("reserved") ? (description + "#" + intValue) : description; } } @Override public int hashCode() { if (stringValue != null) { return stringValue.hashCode(); } else { return intValue; } } @Override public boolean equals(Object object) { if (this.getClass() != object.getClass()) { return false; } Enumeration e = (Enumeration) object; if (stringValue != null) { return this.stringValue.equals(e.stringValue); } else { return this.intValue == e.intValue; } } protected static boolean equals(int i1, int i2) { return i1 == i2; } protected static boolean equals(int i1[], int i2) { return i1[0] == i2; } protected static boolean equals(String s1, String s2) { if (s1 == s2) { return true; } if (s1 == null) { return false; } return trim(s1).equals(trim(s2)); } private static String trim(String s) { return (s == null) ? null : s.trim(); // TODO: also remove leading zeros } } /** * ***************************************************************************************************************** * * <p>For de-serialization and for supporting CGLIB only. CGLIB is needed by blueMarine. For this * reason it must be public too. * * <p>**************************************************************************************************************** */ public Directory() {} /** * ***************************************************************************************************************** * * <p>Creates a new <code>Directory</code> whose getTags belong to the given registry. * * @param tagRegistry the registry * <p>**************************************************************************************************************** */ protected Directory(@Nonnull final TagRegistry tagRegistry) { this.tagRegistry = tagRegistry; registryName = tagRegistry.getName(); } /** * ***************************************************************************************************************** * * <p>Returns the registry the contained getTags belong to. * * @return the registry * <p>**************************************************************************************************************** */ @Nonnull public TagRegistry getRegistry() { return tagRegistry; } /** * ***************************************************************************************************************** * * <p>**************************************************************************************************************** */ @Nonnegative public long getStart() { return start; } /** * ***************************************************************************************************************** * * <p>**************************************************************************************************************** */ @Nonnegative public long getEnd() { return end; } /** * ***************************************************************************************************************** * * @param iis * @param offset * @throws IOException * <p>**************************************************************************************************************** */ public abstract long load(@Nonnull final RAWImageInputStream iis, long offset) throws IOException; /** * ***************************************************************************************************************** * * @param iis * @param offset * @throws IOException * <p>**************************************************************************************************************** */ public void loadAll(@Nonnull final RAWImageInputStream iis, @Nonnull final long offset) throws IOException { load(iis, offset); } /** * ***************************************************************************************************************** * * <p>Adds a tag to this <code>Directory</code>. * * @param tag the tag to add * <p>**************************************************************************************************************** */ public void addTag(@Nonnull final AbstractTag tag) { if (tag != null) { final int key = tag.getCode(); tagMapByKey.put(tagRegistry.getKey(key), tag); keyList.add(tagRegistry.getKey(key)); } } /** * ***************************************************************************************************************** * * <p>Retrieves a contained tag given its key. * * @param key the tag key * <p>**************************************************************************************************************** */ @CheckForNull public AbstractTag getTag(@Nonnull final Object key) { return tagMapByKey.get(tagRegistry.getKey(key)); } /** * ***************************************************************************************************************** * * <p>Checks if this <code>Directory</code> contains a given tag. * * @param key the tag key * @return true if this <code>Directory</code> contains the tag * <p>**************************************************************************************************************** */ public boolean containsTag(@Nonnull final Object key) { return (tagRegistry != null) && tagMapByKey.containsKey(tagRegistry.getKey(key)); } /** * ***************************************************************************************************************** * * <p>Removes a given tag from this </code>Directory</code> * * @param key the tag key * @return the remove tag * <p>**************************************************************************************************************** */ @CheckForNull public AbstractTag removeTag(@Nonnull final Object key) { keyList.remove(tagRegistry.getKey(key)); return tagMapByKey.remove(tagRegistry.getKey(key)); } /** * ***************************************************************************************************************** * * <p>Removes all tags from this /code>Directory</code>. * * <p>**************************************************************************************************************** */ public void removeAllTags() { keyList.clear(); tagMapByKey.clear(); } /** * ***************************************************************************************************************** * * <p>Returns the name of the given tag. FIXME: is meaningful here? * * @param key the tag key * @return the tag name * <p>**************************************************************************************************************** */ @Nonnull public String getTagName(@Nonnull final Object key) { return tagRegistry.getTagName(((Number) key).intValue()); } /** * ***************************************************************************************************************** * * <p>FIXME: This method is only provided for supporting it.tidalwave.image.DirectorySupport, but * should be replaced with a more decoupled mechanism. This method returns a plain Number when an * enumeration could be expected. * * <p>**************************************************************************************************************** */ @CheckForNull public Object getObject(@Nonnull final Object key) { final AbstractTag tag = getTag(key); return (tag != null) ? tag.getValue() : null; } /** * ***************************************************************************************************************** * * <p>Returns the tag values as bytes. * * @param key the tag key * @return the bytes or null if the tag is not present * <p>**************************************************************************************************************** */ @CheckForNull public byte[] getBytes(@Nonnull final Object key) { final AbstractTag tag = getTag(key); return (tag != null) ? tag.getByteValues() : null; } /** * ***************************************************************************************************************** * * <p>Returns the tag value as a byte. * * @param key the tag key * @return the byte * @throws NoSuchElementException if the tag is not present * <p>**************************************************************************************************************** */ public int getByte(@Nonnull final Object key) { if (!containsTag(key)) { throw new NoSuchElementException("No tags with key = " + key); } return getBytes(key)[0]; } /** * ***************************************************************************************************************** * * <p>Returns the tag values as rationals. * * @param key the tag key * @return the rationals or null if the tag is not present * <p>**************************************************************************************************************** */ @CheckForNull public TagRational[] getRationals(@Nonnull final Object key) { AbstractTag tag = getTag(key); return (tag != null) ? tag.getRationalValues() : null; } /** * ***************************************************************************************************************** * * <p>Returns the tag value as a rational. * * @param key the tag key * @return the rational * @throws NoSuchElementException if the tag is not present * <p>**************************************************************************************************************** */ @Nonnull public TagRational getRational(@Nonnull final Object key) { if (!containsTag(key)) { throw new NoSuchElementException("No tags with key = " + key); } return getRationals(key)[0]; } /** * ***************************************************************************************************************** * * <p>Returns the tag values as doubles. * * @param key the tag key * @return the doubles or null if the tag is not present * <p>**************************************************************************************************************** */ @CheckForNull public double[] getDoubles(@Nonnull final Object key) { final AbstractTag tag = getTag(key); return (tag != null) ? asDoubles(tag.getRationalValues()) : null; } /** * ***************************************************************************************************************** * * <p>Returns the tag value as a double. * * @param key the tag key * @return the double * @throws NoSuchElementException if the tag is not present * <p>**************************************************************************************************************** */ public double getDouble(@Nonnull final Object key) { if (!containsTag(key)) { throw new NoSuchElementException("No tags with key = " + key); } return getRationals(key)[0].doubleValue(); } /** * ***************************************************************************************************************** * * <p>Returns the tag values as floats. * * @param key the tag key * @return the floats or null if the tag is not present * <p>**************************************************************************************************************** */ @CheckForNull public float[] getFloats(@Nonnull final Object key) { final AbstractTag tag = getTag(key); return (tag != null) ? tag.getFloatValues() : null; } /** * ***************************************************************************************************************** * * <p>Returns the tag value as a float. * * @param key the tag key * @return the float * @throws NoSuchElementException if the tag is not present * <p>**************************************************************************************************************** */ public float getFloat(@Nonnull final Object key) { if (!containsTag(key)) { throw new NoSuchElementException("No tags with key = " + key); } return getRationals(key)[0].floatValue(); } /** * ***************************************************************************************************************** * * <p>Returns the tag values as integers. * * @param key the tag key * @return the integers or null if the tag is not present * <p>**************************************************************************************************************** */ @CheckForNull public int[] getIntegers(@Nonnull final Object key) { final AbstractTag tag = getTag(key); if (tag == null) { return null; } int[] intValues = tag.getIntValues(); if (intValues != null) { return intValues; } final byte[] byteValues = tag.getByteValues(); if (byteValues != null) { intValues = new int[byteValues.length]; for (int i = 0; i < byteValues.length; i++) { intValues[i] = byteValues[i] & 0xff; } return intValues; } return null; } /** * ***************************************************************************************************************** * * <p>Returns the tag value as an integer. <br> * This method returns 0 if there exists a tag with the given key which contains an empty array of * integers. Such a tag is BayerGreenSplit in DNG files. It should be clarified if this is * compliant with specs (in which case this behaviour should be extended to other multiple-value * getXXX() methods, or if it is a bug of Adobe Converter. * * @param key the tag key * @return the integer * @throws NoSuchElementException if the tag is not present * <p>**************************************************************************************************************** */ public int getInteger(@Nonnull final Object key) throws NoSuchElementException { if (!containsTag(key)) { throw new NoSuchElementException("No tags with key = " + key); } int[] i = getIntegers(key); if (i.length == 0) // FIXME: happens with a BayerGreenSplit field in DNG files { return 0; } return i[0]; } /** * ***************************************************************************************************************** * * <p>Returns the tag value as a string. * * @param key the tag key * @return the string or null if the tag has not been found * <p>**************************************************************************************************************** */ @CheckForNull public String getString(@Nonnull final Object key) { final AbstractTag tag = getTag(key); return (tag != null) ? tag.getASCIIValue() : null; } /** * ***************************************************************************************************************** * * <p>**************************************************************************************************************** */ @CheckForNull public Directory getNextDirectory() { return nextDirectory; } /** * ***************************************************************************************************************** * * <p>Adds a sub-directory. * * @param subDirectory the sub-directory * <p>**************************************************************************************************************** */ public void addDirectory(@Nonnull final Directory subDirectory) { directoryList.add(subDirectory); } /** * ***************************************************************************************************************** * * <p>Returns an iterator over sub-directories. Note that named directories are not returned. * * @return the yterator * <p>**************************************************************************************************************** */ @Nonnull public Collection<Directory> getSubDirectories() { return Collections.unmodifiableCollection(directoryList); } /** * ***************************************************************************************************************** * * <p>Adds a named sub-directory. * * @param name the directory name * @param subDirectory the sub-directory * <p>**************************************************************************************************************** */ public void addNamedDirectory(@Nonnull final String name, @Nonnull final Directory subDirectory) { directoryMapByName.put(name, subDirectory); } /** * ***************************************************************************************************************** * * <p>Returns a named sub-directory * * @param name the sub-directory name * @return the sub-directory * <p>**************************************************************************************************************** */ @CheckForNull public Directory getNamedDirectory(@Nonnull final String name) { return directoryMapByName.get(name); } /** * ***************************************************************************************************************** * * <p>Returns the names of named sub directories * * @return the subdirectory names * <p>**************************************************************************************************************** */ @CheckForNull public String[] getSubDirectoryNames() { return directoryMapByName.keySet().toArray(new String[0]); } /** * ***************************************************************************************************************** * * <p>Returns the contained getTags in the same order as they were added. * * @return the contained getTags * <p>**************************************************************************************************************** */ @Nonnull public Collection<AbstractTag> getTags() { final List<AbstractTag> result = new ArrayList<AbstractTag>(); for (final Object key : keyList) { result.add(tagMapByKey.get(key)); } return result; } /** * ***************************************************************************************************************** * * <p>Convenience method that converts an array of rationals into floats. * * @param rationals * @return the floats * <p>**************************************************************************************************************** */ @CheckForNull public static float[] asFloats(@CheckForNull final TagRational[] rationals) { if (rationals == null) { return null; } float[] floats = new float[rationals.length]; for (int i = 0; i < rationals.length; i++) { floats[i] = rationals[i].floatValue(); } return floats; } /** * ***************************************************************************************************************** * * <p>Convenience method that converts an array of rationals into doubles. * * @param rationals * @return the doubles * <p>**************************************************************************************************************** */ @CheckForNull public static double[] asDoubles(@CheckForNull final TagRational[] rationals) { if (rationals == null) { return null; } double[] doubles = new double[rationals.length]; for (int i = 0; i < rationals.length; i++) { doubles[i] = rationals[i].doubleValue(); } return doubles; } /** * ***************************************************************************************************************** * * @inheritDoc * **************************************************************************************************************** */ @Override @Nonnull public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append(String.format("%d - %d (0x%x - 0x%x)", start, end, start, end)); for (final Object key : keyList) { buffer.append("\n>>>>>>>> "); final Object value = tagMapByKey.get(key); buffer.append(value); } buffer.append("\n"); return buffer.toString(); } /** * ***************************************************************************************************************** * * <p>**************************************************************************************************************** */ @Nonnull protected String toString(@Nonnull final byte[] array) { if (array.length > 64) { return "" + array.length + " bytes"; } final StringBuilder buffer = new StringBuilder(""); for (int i = 0; i < array.length; i++) { if (i > 0) { buffer.append(","); } buffer.append(Integer.toHexString(array[i] & 0xff)); } return buffer.toString(); } /** * ***************************************************************************************************************** * * <p>**************************************************************************************************************** */ @Nonnull protected String toString(@Nonnull final int[] array) { if (array.length > 64) { return "" + array.length + " integers"; } final StringBuilder buffer = new StringBuilder(""); for (int i = 0; i < array.length; i++) { if (i > 0) { buffer.append(","); } buffer.append(Integer.toString(array[i])); } return buffer.toString(); } /** * ***************************************************************************************************************** * * <p>**************************************************************************************************************** */ @Nonnull protected String toString(@Nonnull final double[] array) { if (array.length > 64) { return "" + array.length + " doubles"; } final StringBuilder buffer = new StringBuilder(""); for (int i = 0; i < array.length; i++) { if (i > 0) { buffer.append(","); } buffer.append(Double.toString(array[i])); } return buffer.toString(); } /** * ***************************************************************************************************************** * * <p>**************************************************************************************************************** */ @Nonnull protected String toString(@Nonnull final TagRational[] array) { final StringBuilder buffer = new StringBuilder(""); for (int i = 0; i < array.length; i++) { if (i > 0) { buffer.append(","); } buffer.append(array[i].toString()); } return buffer.toString(); } /** * ***************************************************************************************************************** * * <p>Customized deserialization code. This method restores the link to the registry this tag * belongs to. * * @param is * @throws IOException * @throws ClassNotFoundException * <p>**************************************************************************************************************** */ private void readObject(@Nonnull final ObjectInputStream is) throws IOException, ClassNotFoundException { is.defaultReadObject(); tagRegistry = TagRegistry.getRegistry(registryName); } }
/** * ********************************************************************************************************************* * * @author fritz * @version $Id$ * <p>******************************************************************************************************************** */ public class CRWImageInputStream extends RAWImageInputStreamImpl { private static final String CLASS = CRWImageInputStream.class.getName(); private static final Logger logger = Logger.getLogger(CLASS); private static final String[] THM_EXTENSIONS = {"THM", "Thm", "thm"}; private ImageInputStream crwInputStream; private ImageInputStream thmInputStream; /** * ***************************************************************************************************************** * * @param delegate * @throws IOException * <p>***************************************************************************** */ public CRWImageInputStream(ImageInputStream delegate) throws IOException { super(delegate); crwInputStream = delegate; if (delegate instanceof FileImageInputStream2) { File file = ((FileImageInputStream2) delegate).getFile(); File thmFile = null; String path = file.getAbsolutePath(); int i = path.lastIndexOf('.'); if (i > 0) { path = path.substring(0, i + 1); } for (i = 0; i < THM_EXTENSIONS.length; i++) { String thmPath = path + THM_EXTENSIONS[i]; thmFile = new File(thmPath); if (thmFile.exists()) { break; } } if (!thmFile.exists()) { logger.warning("File " + thmFile + " does not exist"); } else { logger.fine("THM file is %s", thmFile); thmInputStream = new FileImageInputStream(thmFile); } } else { logger.warning("delegate is " + delegate + ", won't be able to manage .THM file"); } } /** * ***************************************************************************************************************** * * <p>***************************************************************************** */ public void switchToCRWStream() { super.delegate = crwInputStream; } /** * ***************************************************************************************************************** * * <p>***************************************************************************** */ public boolean switchToTHMStream() { if (thmInputStream != null) { super.delegate = thmInputStream; return true; } return false; } /** * ***************************************************************************************************************** * * @inheritDoc ***************************************************************************** */ @Override public void close() throws IOException { try { super.close(); } finally { if (thmInputStream != null) { thmInputStream.close(); // TODO: should catch exceptions here? } } } }