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

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

mercurial