mobile/android/base/favicons/decoders/IconDirectoryEntry.java

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mobile/android/base/favicons/decoders/IconDirectoryEntry.java	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,201 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +package org.mozilla.gecko.favicons.decoders;
     1.9 +
    1.10 +/**
    1.11 + * Representation of an ICO file ICONDIRENTRY structure.
    1.12 + */
    1.13 +public class IconDirectoryEntry implements Comparable<IconDirectoryEntry> {
    1.14 +
    1.15 +    public static int maxBPP;
    1.16 +
    1.17 +    int width;
    1.18 +    int height;
    1.19 +    int paletteSize;
    1.20 +    int bitsPerPixel;
    1.21 +    int payloadSize;
    1.22 +    int payloadOffset;
    1.23 +    boolean payloadIsPNG;
    1.24 +
    1.25 +    // Tracks the index in the Icon Directory of this entry. Useful only for pruning.
    1.26 +    int index;
    1.27 +    boolean isErroneous;
    1.28 +
    1.29 +    public IconDirectoryEntry(int width, int height, int paletteSize, int bitsPerPixel, int payloadSize, int payloadOffset, boolean payloadIsPNG) {
    1.30 +        this.width = width;
    1.31 +        this.height = height;
    1.32 +        this.paletteSize = paletteSize;
    1.33 +        this.bitsPerPixel = bitsPerPixel;
    1.34 +        this.payloadSize = payloadSize;
    1.35 +        this.payloadOffset = payloadOffset;
    1.36 +        this.payloadIsPNG = payloadIsPNG;
    1.37 +    }
    1.38 +
    1.39 +    /**
    1.40 +     * Method to get a dummy Icon Directory Entry with the Erroneous bit set.
    1.41 +     *
    1.42 +     * @return An erroneous placeholder Icon Directory Entry.
    1.43 +     */
    1.44 +    public static IconDirectoryEntry getErroneousEntry() {
    1.45 +        IconDirectoryEntry ret = new IconDirectoryEntry(-1, -1, -1, -1, -1, -1, false);
    1.46 +        ret.isErroneous = true;
    1.47 +
    1.48 +        return ret;
    1.49 +    }
    1.50 +
    1.51 +    /**
    1.52 +     * Create an IconDirectoryEntry object from a byte[]. Interprets the buffer starting at the given
    1.53 +     * offset as an IconDirectoryEntry and returns the result.
    1.54 +     *
    1.55 +     * @param buffer Byte array containing the icon directory entry to decode.
    1.56 +     * @param regionOffset Offset into the byte array of the valid region of the buffer.
    1.57 +     * @param regionLength Length of the valid region in the buffer.
    1.58 +     * @param entryOffset Offset of the icon directory entry to decode within the buffer.
    1.59 +     * @return An IconDirectoryEntry object representing the entry specified, or null if the entry
    1.60 +     *         is obviously invalid.
    1.61 +     */
    1.62 +    public static IconDirectoryEntry createFromBuffer(byte[] buffer, int regionOffset, int regionLength, int entryOffset) {
    1.63 +        // Verify that the reserved field is really zero.
    1.64 +        if (buffer[entryOffset + 3] != 0) {
    1.65 +            return getErroneousEntry();
    1.66 +        }
    1.67 +
    1.68 +        // Verify that the entry points to a region that actually exists in the buffer, else bin it.
    1.69 +        int fieldPtr = entryOffset + 8;
    1.70 +        int entryLength = (buffer[fieldPtr] & 0xFF) |
    1.71 +                          (buffer[fieldPtr + 1] & 0xFF) << 8 |
    1.72 +                          (buffer[fieldPtr + 2] & 0xFF) << 16 |
    1.73 +                          (buffer[fieldPtr + 3] & 0xFF) << 24;
    1.74 +
    1.75 +        // Advance to the offset field.
    1.76 +        fieldPtr += 4;
    1.77 +
    1.78 +        int payloadOffset = (buffer[fieldPtr] & 0xFF) |
    1.79 +                            (buffer[fieldPtr + 1] & 0xFF) << 8 |
    1.80 +                            (buffer[fieldPtr + 2] & 0xFF) << 16 |
    1.81 +                            (buffer[fieldPtr + 3] & 0xFF) << 24;
    1.82 +
    1.83 +        // Fail if the entry describes a region outside the buffer.
    1.84 +        if (payloadOffset < 0 || entryLength < 0 || payloadOffset + entryLength > regionOffset + regionLength) {
    1.85 +            return getErroneousEntry();
    1.86 +        }
    1.87 +
    1.88 +        // Extract the image dimensions.
    1.89 +        int imageWidth = buffer[entryOffset] & 0xFF;
    1.90 +        int imageHeight = buffer[entryOffset+1] & 0xFF;
    1.91 +
    1.92 +        // Because Microsoft, a size value of zero represents an image size of 256.
    1.93 +        if (imageWidth == 0) {
    1.94 +            imageWidth = 256;
    1.95 +        }
    1.96 +
    1.97 +        if (imageHeight == 0) {
    1.98 +            imageHeight = 256;
    1.99 +        }
   1.100 +
   1.101 +        // If the image uses a colour palette, this is the number of colours, otherwise this is zero.
   1.102 +        int paletteSize = buffer[entryOffset + 2] & 0xFF;
   1.103 +
   1.104 +        // The plane count - usually 0 or 1. When > 1, taken as multiplier on bitsPerPixel.
   1.105 +        int colorPlanes = buffer[entryOffset + 4] & 0xFF;
   1.106 +
   1.107 +        int bitsPerPixel = (buffer[entryOffset + 6] & 0xFF) |
   1.108 +                           (buffer[entryOffset + 7] & 0xFF) << 8;
   1.109 +
   1.110 +        if (colorPlanes > 1) {
   1.111 +            bitsPerPixel *= colorPlanes;
   1.112 +        }
   1.113 +
   1.114 +        // Look for PNG magic numbers at the start of the payload.
   1.115 +        boolean payloadIsPNG = FaviconDecoder.bufferStartsWith(buffer, FaviconDecoder.ImageMagicNumbers.PNG.value, regionOffset + payloadOffset);
   1.116 +
   1.117 +        return new IconDirectoryEntry(imageWidth, imageHeight, paletteSize, bitsPerPixel, entryLength, payloadOffset, payloadIsPNG);
   1.118 +    }
   1.119 +
   1.120 +    /**
   1.121 +     * Get the number of bytes from the start of the ICO file to the beginning of this entry.
   1.122 +     */
   1.123 +    public int getOffset() {
   1.124 +        return ICODecoder.ICO_HEADER_LENGTH_BYTES + (index * ICODecoder.ICO_ICONDIRENTRY_LENGTH_BYTES);
   1.125 +    }
   1.126 +
   1.127 +    @Override
   1.128 +    public int compareTo(IconDirectoryEntry another) {
   1.129 +        if (width > another.width) {
   1.130 +            return 1;
   1.131 +        }
   1.132 +
   1.133 +        if (width < another.width) {
   1.134 +            return -1;
   1.135 +        }
   1.136 +
   1.137 +        // Where both images exceed the max BPP, take the smaller of the two BPP values.
   1.138 +        if (bitsPerPixel >= maxBPP && another.bitsPerPixel >= maxBPP) {
   1.139 +            if (bitsPerPixel < another.bitsPerPixel) {
   1.140 +                return 1;
   1.141 +            }
   1.142 +
   1.143 +            if (bitsPerPixel > another.bitsPerPixel) {
   1.144 +                return -1;
   1.145 +            }
   1.146 +        }
   1.147 +
   1.148 +        // Otherwise, take the larger of the BPP values.
   1.149 +        if (bitsPerPixel > another.bitsPerPixel) {
   1.150 +            return 1;
   1.151 +        }
   1.152 +
   1.153 +        if (bitsPerPixel < another.bitsPerPixel) {
   1.154 +            return -1;
   1.155 +        }
   1.156 +
   1.157 +        // Prefer large palettes.
   1.158 +        if (paletteSize > another.paletteSize) {
   1.159 +            return 1;
   1.160 +        }
   1.161 +
   1.162 +        if (paletteSize < another.paletteSize) {
   1.163 +            return -1;
   1.164 +        }
   1.165 +
   1.166 +        // Prefer smaller payloads.
   1.167 +        if (payloadSize < another.payloadSize) {
   1.168 +            return 1;
   1.169 +        }
   1.170 +
   1.171 +        if (payloadSize > another.payloadSize) {
   1.172 +            return -1;
   1.173 +        }
   1.174 +
   1.175 +        // If all else fails, prefer PNGs over BMPs. They tend to be smaller.
   1.176 +        if (payloadIsPNG && !another.payloadIsPNG) {
   1.177 +            return 1;
   1.178 +        }
   1.179 +
   1.180 +        if (!payloadIsPNG && another.payloadIsPNG) {
   1.181 +            return -1;
   1.182 +        }
   1.183 +
   1.184 +        return 0;
   1.185 +    }
   1.186 +
   1.187 +    public static void setMaxBPP(int maxBPP) {
   1.188 +        IconDirectoryEntry.maxBPP = maxBPP;
   1.189 +    }
   1.190 +
   1.191 +    @Override
   1.192 +    public String toString() {
   1.193 +        return "IconDirectoryEntry{" +
   1.194 +                "\nwidth=" + width +
   1.195 +                ", \nheight=" + height +
   1.196 +                ", \npaletteSize=" + paletteSize +
   1.197 +                ", \nbitsPerPixel=" + bitsPerPixel +
   1.198 +                ", \npayloadSize=" + payloadSize +
   1.199 +                ", \npayloadOffset=" + payloadOffset +
   1.200 +                ", \npayloadIsPNG=" + payloadIsPNG +
   1.201 +                ", \nindex=" + index +
   1.202 +                '}';
   1.203 +    }
   1.204 +}

mercurial