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

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko.favicons.decoders;
michael@0 6
michael@0 7 /**
michael@0 8 * Representation of an ICO file ICONDIRENTRY structure.
michael@0 9 */
michael@0 10 public class IconDirectoryEntry implements Comparable<IconDirectoryEntry> {
michael@0 11
michael@0 12 public static int maxBPP;
michael@0 13
michael@0 14 int width;
michael@0 15 int height;
michael@0 16 int paletteSize;
michael@0 17 int bitsPerPixel;
michael@0 18 int payloadSize;
michael@0 19 int payloadOffset;
michael@0 20 boolean payloadIsPNG;
michael@0 21
michael@0 22 // Tracks the index in the Icon Directory of this entry. Useful only for pruning.
michael@0 23 int index;
michael@0 24 boolean isErroneous;
michael@0 25
michael@0 26 public IconDirectoryEntry(int width, int height, int paletteSize, int bitsPerPixel, int payloadSize, int payloadOffset, boolean payloadIsPNG) {
michael@0 27 this.width = width;
michael@0 28 this.height = height;
michael@0 29 this.paletteSize = paletteSize;
michael@0 30 this.bitsPerPixel = bitsPerPixel;
michael@0 31 this.payloadSize = payloadSize;
michael@0 32 this.payloadOffset = payloadOffset;
michael@0 33 this.payloadIsPNG = payloadIsPNG;
michael@0 34 }
michael@0 35
michael@0 36 /**
michael@0 37 * Method to get a dummy Icon Directory Entry with the Erroneous bit set.
michael@0 38 *
michael@0 39 * @return An erroneous placeholder Icon Directory Entry.
michael@0 40 */
michael@0 41 public static IconDirectoryEntry getErroneousEntry() {
michael@0 42 IconDirectoryEntry ret = new IconDirectoryEntry(-1, -1, -1, -1, -1, -1, false);
michael@0 43 ret.isErroneous = true;
michael@0 44
michael@0 45 return ret;
michael@0 46 }
michael@0 47
michael@0 48 /**
michael@0 49 * Create an IconDirectoryEntry object from a byte[]. Interprets the buffer starting at the given
michael@0 50 * offset as an IconDirectoryEntry and returns the result.
michael@0 51 *
michael@0 52 * @param buffer Byte array containing the icon directory entry to decode.
michael@0 53 * @param regionOffset Offset into the byte array of the valid region of the buffer.
michael@0 54 * @param regionLength Length of the valid region in the buffer.
michael@0 55 * @param entryOffset Offset of the icon directory entry to decode within the buffer.
michael@0 56 * @return An IconDirectoryEntry object representing the entry specified, or null if the entry
michael@0 57 * is obviously invalid.
michael@0 58 */
michael@0 59 public static IconDirectoryEntry createFromBuffer(byte[] buffer, int regionOffset, int regionLength, int entryOffset) {
michael@0 60 // Verify that the reserved field is really zero.
michael@0 61 if (buffer[entryOffset + 3] != 0) {
michael@0 62 return getErroneousEntry();
michael@0 63 }
michael@0 64
michael@0 65 // Verify that the entry points to a region that actually exists in the buffer, else bin it.
michael@0 66 int fieldPtr = entryOffset + 8;
michael@0 67 int entryLength = (buffer[fieldPtr] & 0xFF) |
michael@0 68 (buffer[fieldPtr + 1] & 0xFF) << 8 |
michael@0 69 (buffer[fieldPtr + 2] & 0xFF) << 16 |
michael@0 70 (buffer[fieldPtr + 3] & 0xFF) << 24;
michael@0 71
michael@0 72 // Advance to the offset field.
michael@0 73 fieldPtr += 4;
michael@0 74
michael@0 75 int payloadOffset = (buffer[fieldPtr] & 0xFF) |
michael@0 76 (buffer[fieldPtr + 1] & 0xFF) << 8 |
michael@0 77 (buffer[fieldPtr + 2] & 0xFF) << 16 |
michael@0 78 (buffer[fieldPtr + 3] & 0xFF) << 24;
michael@0 79
michael@0 80 // Fail if the entry describes a region outside the buffer.
michael@0 81 if (payloadOffset < 0 || entryLength < 0 || payloadOffset + entryLength > regionOffset + regionLength) {
michael@0 82 return getErroneousEntry();
michael@0 83 }
michael@0 84
michael@0 85 // Extract the image dimensions.
michael@0 86 int imageWidth = buffer[entryOffset] & 0xFF;
michael@0 87 int imageHeight = buffer[entryOffset+1] & 0xFF;
michael@0 88
michael@0 89 // Because Microsoft, a size value of zero represents an image size of 256.
michael@0 90 if (imageWidth == 0) {
michael@0 91 imageWidth = 256;
michael@0 92 }
michael@0 93
michael@0 94 if (imageHeight == 0) {
michael@0 95 imageHeight = 256;
michael@0 96 }
michael@0 97
michael@0 98 // If the image uses a colour palette, this is the number of colours, otherwise this is zero.
michael@0 99 int paletteSize = buffer[entryOffset + 2] & 0xFF;
michael@0 100
michael@0 101 // The plane count - usually 0 or 1. When > 1, taken as multiplier on bitsPerPixel.
michael@0 102 int colorPlanes = buffer[entryOffset + 4] & 0xFF;
michael@0 103
michael@0 104 int bitsPerPixel = (buffer[entryOffset + 6] & 0xFF) |
michael@0 105 (buffer[entryOffset + 7] & 0xFF) << 8;
michael@0 106
michael@0 107 if (colorPlanes > 1) {
michael@0 108 bitsPerPixel *= colorPlanes;
michael@0 109 }
michael@0 110
michael@0 111 // Look for PNG magic numbers at the start of the payload.
michael@0 112 boolean payloadIsPNG = FaviconDecoder.bufferStartsWith(buffer, FaviconDecoder.ImageMagicNumbers.PNG.value, regionOffset + payloadOffset);
michael@0 113
michael@0 114 return new IconDirectoryEntry(imageWidth, imageHeight, paletteSize, bitsPerPixel, entryLength, payloadOffset, payloadIsPNG);
michael@0 115 }
michael@0 116
michael@0 117 /**
michael@0 118 * Get the number of bytes from the start of the ICO file to the beginning of this entry.
michael@0 119 */
michael@0 120 public int getOffset() {
michael@0 121 return ICODecoder.ICO_HEADER_LENGTH_BYTES + (index * ICODecoder.ICO_ICONDIRENTRY_LENGTH_BYTES);
michael@0 122 }
michael@0 123
michael@0 124 @Override
michael@0 125 public int compareTo(IconDirectoryEntry another) {
michael@0 126 if (width > another.width) {
michael@0 127 return 1;
michael@0 128 }
michael@0 129
michael@0 130 if (width < another.width) {
michael@0 131 return -1;
michael@0 132 }
michael@0 133
michael@0 134 // Where both images exceed the max BPP, take the smaller of the two BPP values.
michael@0 135 if (bitsPerPixel >= maxBPP && another.bitsPerPixel >= maxBPP) {
michael@0 136 if (bitsPerPixel < another.bitsPerPixel) {
michael@0 137 return 1;
michael@0 138 }
michael@0 139
michael@0 140 if (bitsPerPixel > another.bitsPerPixel) {
michael@0 141 return -1;
michael@0 142 }
michael@0 143 }
michael@0 144
michael@0 145 // Otherwise, take the larger of the BPP values.
michael@0 146 if (bitsPerPixel > another.bitsPerPixel) {
michael@0 147 return 1;
michael@0 148 }
michael@0 149
michael@0 150 if (bitsPerPixel < another.bitsPerPixel) {
michael@0 151 return -1;
michael@0 152 }
michael@0 153
michael@0 154 // Prefer large palettes.
michael@0 155 if (paletteSize > another.paletteSize) {
michael@0 156 return 1;
michael@0 157 }
michael@0 158
michael@0 159 if (paletteSize < another.paletteSize) {
michael@0 160 return -1;
michael@0 161 }
michael@0 162
michael@0 163 // Prefer smaller payloads.
michael@0 164 if (payloadSize < another.payloadSize) {
michael@0 165 return 1;
michael@0 166 }
michael@0 167
michael@0 168 if (payloadSize > another.payloadSize) {
michael@0 169 return -1;
michael@0 170 }
michael@0 171
michael@0 172 // If all else fails, prefer PNGs over BMPs. They tend to be smaller.
michael@0 173 if (payloadIsPNG && !another.payloadIsPNG) {
michael@0 174 return 1;
michael@0 175 }
michael@0 176
michael@0 177 if (!payloadIsPNG && another.payloadIsPNG) {
michael@0 178 return -1;
michael@0 179 }
michael@0 180
michael@0 181 return 0;
michael@0 182 }
michael@0 183
michael@0 184 public static void setMaxBPP(int maxBPP) {
michael@0 185 IconDirectoryEntry.maxBPP = maxBPP;
michael@0 186 }
michael@0 187
michael@0 188 @Override
michael@0 189 public String toString() {
michael@0 190 return "IconDirectoryEntry{" +
michael@0 191 "\nwidth=" + width +
michael@0 192 ", \nheight=" + height +
michael@0 193 ", \npaletteSize=" + paletteSize +
michael@0 194 ", \nbitsPerPixel=" + bitsPerPixel +
michael@0 195 ", \npayloadSize=" + payloadSize +
michael@0 196 ", \npayloadOffset=" + payloadOffset +
michael@0 197 ", \npayloadIsPNG=" + payloadIsPNG +
michael@0 198 ", \nindex=" + index +
michael@0 199 '}';
michael@0 200 }
michael@0 201 }

mercurial