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    package org.hd.d.pg2k.svrCore.mediahandler;
030    
031    import java.awt.Dimension;
032    import java.io.DataInputStream;
033    import java.io.IOException;
034    import java.io.InputStream;
035    
036    import org.hd.d.pg2k.svrCore.MIME.AbstractImageHandler;
037    import org.hd.d.pg2k.svrCore.MIME.ExhibitMIME;
038    
039    /**Default handler for png file (MIME type image/png).
040     * Relies on Java Advanced Imaging extension JAI 1.1.2 or newer
041     * (javax.media.jai.*) for some functionality (eg making thumbnails);
042     * may not be able to run (or possibly even load) without it,
043     * though we try to ensure that basic functionality is available anyway.
044     */
045    public final class png extends AbstractImageHandler
046        {
047        /** Get internal type of ExhibitMIME type; never null. */
048        public ExhibitMIME.ExhibitTypeParameters getExhibitType()
049            { return(ExhibitMIME.getParamsByType(ExhibitMIME.ET_PNG)); }
050    
051        /**Get dimensions X and Y of a PNG exhibit, else null if dimensions cannot be computed.
052         * This reads the opening bytes of the exhibit directly to extract the
053         * dimensions, rather than loading and interpreting the whole image.
054         * <p>
055         * This input stream must be of the correct type, eg the magic number
056         * already checked.
057         * <p>
058         * This <strong>does not close its input stream</strong> when done.
059         *
060         * @param is        the exhibit as a binary data stream
061         *
062         * @throws IOException  in case of problems with corrupt data
063         *     (or a broken exhibit)
064         */
065        @Override public Dimension get2DImageDimensions(final InputStream is)
066            throws IOException
067            {
068            final DataInputStream dis = new DataInputStream(is);
069            final byte b[] = new byte[24]; // Exactly enough for what we want.
070            dis.readFully(b);
071            // We could check that bytes 8 to 15 are:
072            //    \0 \0 \0 \r I H D R
073            // The width and height are 4-byte unsigned network-order
074            // starting at offset 16.
075            final Dimension result = new Dimension();
076            result.width  = ((b[16]&0xff) << 24) +
077                            ((b[17]&0xff) << 16) +
078                            ((b[18]&0xff) <<  8) +
079                            ((b[19]&0xff));
080            result.height = ((b[20]&0xff) << 24) +
081                            ((b[21]&0xff) << 16) +
082                            ((b[22]&0xff) <<  8) +
083                            ((b[23]&0xff));
084            return(result); // DONE!
085            }
086    
087        /**Target bits-per-pixel for thumbnail images; strictly positive.
088         * Does not need to leave all images perfect, but most should be OK.
089         * Note that we'll be limited from above by the absolute size limits
090         * of thumbnails in general.
091         * <p>
092         * A value in the range 4--8 is probably good.
093         */
094        private static final int TARGET_BPP = 6;
095    
096        /**Estimated file overhead constant, ie what would a 0x0 file size be; non-negative.
097         * This factor helps avoid penalising small images by
098         * having the overhead eat into the bits that should be being used
099         * by the image.
100         * <p>
101         * One byte less than a 1x1 white image produced by PSP7.
102         */
103        private static final int FILE_OVERHEAD = 193;
104    
105        /**Estimated (maximum) bytes per pixel of a decoded image in memory.
106         * This assumes that a 24/32-bit RGB or ARGB format is the most expensive
107         * we will run across.  (The other main possibility is an 8-bit greyscale.)
108         */
109        private static final int EST_BYTES_PER_IMAGE_PIXEL_IN_MEMORY = 4;
110    
111        /**Maximum quality allowed in a thumbnail; taken to be total colour depth.
112         * Assume that this is the same as the maximum number of bits in memory
113         * we expect an image to take.
114         */
115        private static final int maxThumbnailQuality =
116            8 * EST_BYTES_PER_IMAGE_PIXEL_IN_MEMORY;
117    
118        /**Normal/initial quality for a thumbnail.
119         * This is reasonable for most images, though erring slightly on
120         * the side of retention of image quality at cost of download speed.
121         */
122        private static final int normalThumbnailQuality = TARGET_BPP;
123    
124        /**Maximum quality allowed in a thumbnail; taken to be total colour depth.
125         * Must be positive and can be as little as monochrome, ie one bit.
126         */
127        private static final int minThumbnailQuality = 1;
128    
129        /**Gather together thumbnail parameters. */
130        private static final AbstractImageHandler.ThumbnailParams thumbnailParams =
131            new ThumbnailParams(EST_BYTES_PER_IMAGE_PIXEL_IN_MEMORY,
132                minThumbnailQuality,
133                maxThumbnailQuality,
134                normalThumbnailQuality,
135                TARGET_BPP,
136                FILE_OVERHEAD);
137    
138        @Override public AbstractImageHandler.ThumbnailParams getThumbnailParams()
139            { return(thumbnailParams); }
140    
141        /**Returns true because handler can make thumbnails for this type. */
142        @Override public boolean canMakeThumbnails()
143            { return(true); }
144    
145        // Inherit javadoc.
146        @Override protected int _reduceColoursQualityThreshold() { return(24); }
147        }