001    /*
002    Copyright (c) 1996-2012, Damon Hart-Davis
003    All rights reserved.
004    
005    Redistribution and use in source and binary forms, with or without
006    modification, are permitted provided that the following conditions are
007    met:
008    
009      * Redistributions of source code must retain the above copyright
010        notice, this list of conditions and the following disclaimer.
011    
012      * Redistributions in binary form must reproduce the above copyright
013        notice, this list of conditions and the following disclaimer in the
014        documentation and/or other materials provided with the
015        distribution.
016    
017    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
018    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
019    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
020    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
021    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
022    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
023    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
024    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
025    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
026    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
027    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028    */
029    
030    package org.hd.d.pg2k.svrCore.MIME;
031    
032    import java.awt.image.BufferedImage;
033    import java.io.IOException;
034    import java.io.InputStream;
035    
036    import org.hd.d.pg2k.svrCore.AllExhibitProperties;
037    import org.hd.d.pg2k.svrCore.ExhibitStaticAttr;
038    import org.hd.d.pg2k.svrCore.ExhibitThumbnails;
039    import org.hd.d.pg2k.svrCore.Name.ExhibitFull;
040    import org.w3c.dom.Node;
041    
042    /**
043     * Created by IntelliJ IDEA.
044     * User: Damon Hart-Davis
045     * Date: 29-Aug-2003
046     * Time: 19:36:01
047     */
048    
049    /**Interface for exhibit media-handler classes.
050     * The media handlers do such things as make thumbnails/samples,
051     * and are loaded by name to allow for run-time configuration
052     * and presence/absence of such items as Sun's JPEG support.
053     * <p>
054     * Implementing classes must have an public non-args descriptor
055     * so that we can use Class.forName() to manufacture instances.
056     * <p>
057     * Implementing classes must be immutable and stateless
058     * and thread-safe, so that one instance can be shared between many
059     * threads.
060     * <p>
061     * Both CPU time and memory will be severe run-time constraints on
062     * implementing classes, and they must allow for concurrent operation
063     * with many other instances or internally reduce concurrency
064     * (eg with static locks).
065     * <p>
066     * This interface represents a ``fat'' API, ie implementing classes may not
067     * implement all of the API at run-time but return default/failure
068     * values instead.
069     */
070    public interface Handler
071        {
072        /**Get internal type of ExhibitMIME type; never null. */
073        ExhibitMIME.ExhibitTypeParameters getExhibitType();
074    
075        /**Get X,Y pixel dimensions of an image exhibit, else null if dimensions cannot be computed.
076         * This will always return null for items which do not have a fixed
077         * X,Y size in pixels such as for AU sound files for example.
078         * <p>
079         * For something like a movie in a fixed frame this may be able to
080         * return its dimensions, eg to embed in a page.
081         * <p>
082         * This <strong>does not close its input stream</strong> when done.
083         * <p>
084         * This should generally be efficient, by directly reading (opening) bytes
085         * of the exhibit passed as an input stream, if at all possible,
086         * but generic implementations may be very slow.
087         * <p>
088         * This will only work correctly if the exhibit is of the correct type,
089         * eg its magic number must already have been tested.
090         *
091         * @param is  the exhibit as a binary data stream
092         *
093         * @throws java.io.IOException  in case of problems with corrupt data
094         *     (or a broken exhibit)
095         */
096        java.awt.Dimension get2DImageDimensions(InputStream is)
097            throws IOException;
098    
099        /**Make a byte[]-encoded image of this type constrained above and below by size.
100         * This uses a binary-chop algorithm to attempt to quickly
101         * find the optimal "quality" to make the image at,
102         * lower quality values asking the encoder to discard more information,
103         * eg by doing colour reduction or other quantisation.
104         * <p>
105         * This is not possible for all exhibit types.
106         *
107         * @param upperQualityBound  maximum value of quality to use; non-negative
108         * @param lowerQualityBound  minimum value of quality to use; non-negative
109         *     and no greater than upperQualityBound
110         * @param initialQualityHint  initial suggested quality hint; non-negative,
111         *     no greater than upperQualityBound, no less than lowerQualityBound
112         * @param scaledImage  the input image to encode
113         * @param targetMin  target lower bound, should be higher than absMinSize
114         * @param targetMax  target upper bound, should be lower than absMaxSize
115         * @param absMinSize  absolute minimum number of bytes
116         * @param absMaxSize  absolute maximum number of bytes
117         * @param targetBytes  target number of bytes
118         *
119         * @return encoded image, or null if the image cannot be generated
120         *     with given constraints
121         *
122         * @throws IOException  in case of difficulty generating the image
123         * @throws IllegalArgumentException  if the arguments are invalid
124         */
125        public byte[] makeSizeConstrainedEncodedImage(final int lowerQualityBound,
126                                                   final int initialQualityHint,
127                                                   final int upperQualityBound,
128                                                   final BufferedImage scaledImage,
129                                                   final int targetMin,
130                                                   final int targetMax,
131                                                   final int absMinSize,
132                                                   final int absMaxSize,
133                                                   final int targetBytes)
134            throws IOException,
135                   IllegalArgumentException;
136    
137        /**Get ThumbnailParameters for a particular handler.
138         * A handler that does not build thumbnails
139         * (ie canMakeThumbnails() returns false)
140         * may return null for this, which is the default,
141         * but otherwise this should return non-null.
142         * <p>
143         * This is assumed to be fast, ie to return a fixed static instance
144         * for each handler.
145         * <p>
146         * This is protected, since only the support routines in this base
147         * class need access this data.
148         */
149        public ThumbnailParams getThumbnailParams();
150    
151        /**Returns true if handler can make thumbnails for this type.
152         * makeThumbnails() has to be able to succeed for some real exhibits of
153         * this type,
154         * producing thumbnails/samples of the same MIME type
155         * (though possibly at much-reduced fidelity),
156         * but makeThumbnails() need not succeed for all exhibits of this type.
157         * <p>
158         * By default, returns false, ie thumbnails cannot be made.
159         */
160        boolean canMakeThumbnails();
161    
162        /**Make thumbnails/samples for the specified exhibit.
163         * This may fail with an IOException or return null
164         * to indicate that it is currently unable to create
165         * thumbnails/samples (though this condition may be temporary).
166         * <p>
167         * If this wishes to indicate that it cannot ever make one or more
168         * thumbnails/samples for a given exhibit then this should return a
169         * ExhibitThumbnails object with one or both thumbnails set to null.
170         * <p>
171         * (If canMakeThumbnails() returns false, this should return null.)
172         * <p>
173         * This <strong>does not close its input stream</strong> when done.
174         * <p>
175         * This will only work correctly if the exhibit is of the correct type,
176         * eg its magic number must already have been tested.
177         * <p>
178         * This assumes enough memory and other resource is available.
179         *
180         * @param is  the whole raw image positioned at its start
181         * @param originalLength  is the length of the encoded original in bytes;
182         *     always positive and must reflect the stream input
183         *     and will be constrained to Integer.MAX_VALUE if larger
184         * @return thumbnails, else null if not possible now;
185         *     ExhibitThumbnails.NO_THUMBNAILS may be returned if it looks
186         *     like it never be possible/sensible to make thumbnails for
187         *     this exhibit
188         */
189        ExhibitThumbnails makeThumbnails(InputStream is, long originalLength)
190            throws IOException;
191    
192        /**Make thumbnails/samples for the specified exhibit.
193         * A data source for the exhibit must be supplied,
194         * along with all available properties of that exhibit.
195         * <p>
196         * This may fail with an IOException or return null
197         * to indicate that it is currently unable to create
198         * thumbnails/samples (though this condition may be temporary).
199         * <p>
200         * If this wishes to indicate that it cannot make one or more
201         * thumbnails/samples for a given exhibit then this should return a
202         * ExhibitThumbnails object with one or both thumbnails set to null.
203         * <p>
204         * (If canMakeThumbnails() returns false, this should return null.)
205         * <p>
206         * By default, returns null, ie thumbnails cannot be made.
207         * <p>
208         * This routine regulates memory use, rejecting the attempt
209         * to make the thumbnails if it cannot find/reserve sufficient memory
210         * using MemoryTools.runMemoryIntensiveOperation().
211         * <p>
212         * Calls the InputStream version of makeThumbnails().
213         *
214         * @param unlimitedResources  if true, the generation routine is allowed
215         *     to try to use unlimited resources (especially memory)
216         * @return thumbnails, else null if not possible now;
217         *     ExhibitThumbnails.NO_THUMBNAILS may be returned if it looks
218         *     like it never be possible/sensible to make thumbnails for
219         *     this exhibit
220         */
221        ExhibitThumbnails makeThumbnails(
222                            ExhibitStaticAttr esa,
223                            AllExhibitProperties.ExhibitDataSource eds,
224                            AllExhibitProperties aep,
225                            boolean unlimitedResources)
226            throws IOException;
227    
228    //    /**Get any embedded text, eg comments, in the exhibit.
229    //     * This is to extract comments, creator details, etc,
230    //     * rather than to read the text by OCR, ie this is
231    //     * quick and unambiguous.
232    //     * <p>
233    //     * This should avoid reading the entire image into memory
234    //     * or any other CPU- or memory- intensive operation
235    //     * if at all possible.
236    //     * <p>
237    //     * This <strong>does not close its input stream</strong> when done.
238    //     * <p>
239    //     * The file must be of the correct exhibit type or behaviour
240    //     * is undefined.
241    //     * <p>
242    //     * The default is to return an empty array, ie as if no such
243    //     * embedded text is available.
244    //     *
245    //     * @return an array of non-null embedded text strings within
246    //     *     the exhibit such as copyright notices and comments; never null
247    //     */
248    //    String[] getEmbeddedText(InputStream is)
249    //        throws IOException;
250    
251        /**Decode image as BufferedImage, or null if not possible.
252         * The input stream must be non-null and of the correct exhibit type
253         * else behaviour is undefined.
254         * <p>
255         * This <strong>does not close its input stream</strong> when done.
256         */
257        BufferedImage decodeImage(InputStream is)
258            throws IOException;
259    
260        /**Return image as file-format byte array.
261         * This should only be used for making an image that is compatible
262         * with the output format, eg trying to build a GIF from a true-colour
263         * image is unlikely to work.
264         * <p>
265         * This is used for still images and possible animations
266         * or movies where the bounding rectangle and the contained data
267         * is scaled to the given bounds.
268         * <p>
269         * The output image will, if possible, use the same colour scheme
270         * and other characteristics as the input, though some optional
271         * features that usually consume extra space, such as interlacing,
272         * may be disabled.
273         * <p>
274         * If no output can be generated this returns null,
275         * which is the default behaviour.
276         * <p>
277         * This should adjust the image "quality" with the detail
278         * value (adjusted within the bounds supplied by the handler class)
279         * to try to tune the output size.  For a lossy encoding format
280         * such as JPEG this may be the "quality" factor or compression.
281         * For a lossless format the may have to be the number of
282         * bits-per-pixel that a colour map is reduced to, for example.
283         * <p>
284         * The quality parameter usage is format- and implementation-
285         * dependent, but 0 will generally be the lowest-quality rendering
286         * available and 100 will be the highest, with values in between
287         * monotonically increasing, though that does not mean that there
288         * will be any distinguishable different in the output with
289         * any pair of adjacent (or indeed any) quality values.
290         *
291         * @param imageIn  source image; must not be null and must be suitable
292         *     for the target format
293         * @param quality  desired encoding quality in range 0--100 inclusive,
294         *     with 0 interpreted as "high compression is important,"
295         *     and 100 (or over) interpreted as "high image quality is important.";
296         *     this parameter is ignored where it does not make sense for the format
297         */
298        byte[] makeImageBinary(BufferedImage imageIn, int quality)
299            throws IOException;
300    
301        /**Some constant parameters and hints for thumbnail building.
302         * Each handler that can build thumbnails should define one of these
303         * statically and return it from getThumbnailParameters().
304         * <p>
305         * If canMakeThumbnails() returns true,
306         * getThumbnailParameters() must return a non-null value.
307         * <p>
308         * This exists to help cluttering the base class API with things
309         * relevant only to the internal mechanics of this system.
310         */
311        public static final class ThumbnailParams
312            {
313            /**Estimated bytes-per-pixel of image in memory; positive.
314             * Defaults to 4 assuming RBG/ARGB int format of image.
315             */
316            public final int estimatedBytesPerImagePixelInMemory;
317    
318            /**Minimum usable value of "quality" parameter to makeScaledImage(); non-negative.
319             */
320            public final int minQuality;
321    
322            /**Maximum usable value of "quality" parameter to makeScaledImage().
323             * Anything higher than this value is treated as equal to this value
324             * and implies maximum possible: thus Integer.MAX_VALUE means
325             * maximum possible too.
326             * <p>
327             * If the same as minQuality this indicates that this is not
328             * parametrically variable.
329             */
330            public final int maxQuality;
331    
332            /**Suggested initial quality value.
333             * This will lie between minQuality and maxQuality inclusive.
334             * <p>
335             * Careful choice of this will help reduce the number of
336             * redundant image builds done.
337             */
338            public final int initialQualityHint;
339    
340            /**Suggested target bits-per-pixel for a thumbnail; positive.
341             * This should ignore any typical fixed overheads.
342             * <p>
343             * This is a hint to generation routines.
344             */
345            public final int thumbnailBppHint;
346    
347            /**Approximate minimum overhead of format in bytes; non-negative.
348             * Rough minimum size in this format including magic numbers,
349             * cost of file structure, etc.
350             * <p>
351             * This is used to help make better estimates of target thumbnail
352             * sizes, etc, and need not be (indeed, probably cannot be)
353             * exact given that each format may have different representations.
354             * <p>
355             * Can be zero, though all reasonable implementations should at
356             * least default to the size of any leading "magic" number.
357             */
358            public final int approxMinOverheadBytes;
359    
360            /**Construct an instance.
361             * Argument values are range-checked as far as practical.
362             */
363            public ThumbnailParams(final int estimatedBytesPerImagePixelInMemory,
364                                   final int minQuality,
365                                   final int maxQuality,
366                                   final int initialQualityHint,
367                                   final int thumbnailBppHint,
368                                   final int approxMinOverheadBytes)
369                throws IllegalArgumentException
370                {
371                if(estimatedBytesPerImagePixelInMemory < 1)
372                    { throw new IllegalArgumentException("estimatedBytesPerImagePixelInMemory must be positive"); }
373                if(minQuality < 0)
374                    { throw new IllegalArgumentException("minQuality must be non-negative"); }
375                if(maxQuality < minQuality)
376                    { throw new IllegalArgumentException("maxQuality must no less than minQuality"); }
377                if((initialQualityHint < minQuality) || (initialQualityHint > maxQuality))
378                    { throw new IllegalArgumentException("initialQualityHint must no less than minQuality and no greater than maxQuality"); }
379                if(thumbnailBppHint < 1)
380                    { throw new IllegalArgumentException("thumbnailBppHint must be positive"); }
381                if(approxMinOverheadBytes < 0)
382                    { throw new IllegalArgumentException("approxMinOverheadBytes must be non-negative"); }
383    
384    
385                this.estimatedBytesPerImagePixelInMemory =
386                    estimatedBytesPerImagePixelInMemory;
387                this.minQuality = minQuality;
388                this.maxQuality = maxQuality;
389                this.initialQualityHint = initialQualityHint;
390                this.thumbnailBppHint = thumbnailBppHint;
391                this.approxMinOverheadBytes = approxMinOverheadBytes;
392                }
393            }
394    
395    
396        /**Top-level node name for exhibit meta-data; never null or empty. */
397        public static final String TAG_NAME_METADATA_TOP = "metadata";
398    
399        /**Gets all available exhibit metadata as a single XML DOM tree; null if none.
400         * We do not (yet) have a schema for this and may never do so,
401         * since its structure and content will depend on various external sources
402         * and parts of (for example) the ImageIO and JAI subsystems.
403         * <p>
404         * Under extreme circumstances, eg with damaged exhibit files,
405         * this may throw Error or other RuntimeException values,
406         * though implementations should try to return null rather than do this
407         * if the error appears to be permanent.
408         *
409         * @param is  input stream; never null
410         * @param exhibitName TODO
411         *
412         * @return top-level node "metadata" with captured metadata beneath, else null
413         */
414        public Node getMetadata(final InputStream is, ExhibitFull exhibitName);
415        }