1 /*
2 * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "ICOImageDecoder.h"
33
34 #include <algorithm>
35
36 #include "BMPImageReader.h"
37 #include "PNGImageDecoder.h"
38
39 namespace WebCore {
40
41 // Number of bits in .ICO/.CUR used to store the directory and its entries,
42 // respectively (doesn't match sizeof values for member structs since we omit
43 // some fields).
44 static const size_t sizeOfDirectory = 6;
45 static const size_t sizeOfDirEntry = 16;
46
ICOImageDecoder()47 ICOImageDecoder::ICOImageDecoder()
48 : ImageDecoder()
49 , m_allDataReceived(false)
50 , m_decodedOffset(0)
51 {
52 }
53
~ICOImageDecoder()54 ICOImageDecoder::~ICOImageDecoder()
55 {
56 deleteAllValues(m_bmpReaders);
57 deleteAllValues(m_pngDecoders);
58 }
59
setData(SharedBuffer * data,bool allDataReceived)60 void ICOImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
61 {
62 if (failed())
63 return;
64
65 ImageDecoder::setData(data, allDataReceived);
66 m_allDataReceived = allDataReceived;
67
68 for (BMPReaders::iterator i(m_bmpReaders.begin());
69 i != m_bmpReaders.end(); ++i) {
70 if (*i)
71 (*i)->setData(data);
72 }
73 for (size_t i = 0; i < m_pngDecoders.size(); ++i)
74 setDataForPNGDecoderAtIndex(i);
75 }
76
isSizeAvailable()77 bool ICOImageDecoder::isSizeAvailable()
78 {
79 if (!ImageDecoder::isSizeAvailable())
80 decodeWithCheckForDataEnded(0, true);
81
82 return ImageDecoder::isSizeAvailable();
83 }
84
size() const85 IntSize ICOImageDecoder::size() const
86 {
87 return m_frameSize.isEmpty() ? ImageDecoder::size() : m_frameSize;
88 }
89
frameSizeAtIndex(size_t index) const90 IntSize ICOImageDecoder::frameSizeAtIndex(size_t index) const
91 {
92 return (index && (index < m_dirEntries.size())) ?
93 m_dirEntries[index].m_size : size();
94 }
95
setSize(unsigned width,unsigned height)96 bool ICOImageDecoder::setSize(unsigned width, unsigned height)
97 {
98 if (m_frameSize.isEmpty())
99 return ImageDecoder::setSize(width, height);
100
101 // The size calculated inside the BMPImageReader had better match the one in
102 // the icon directory.
103 if (IntSize(width, height) != m_frameSize)
104 setFailed();
105 return !failed();
106 }
107
frameCount()108 size_t ICOImageDecoder::frameCount()
109 {
110 decodeWithCheckForDataEnded(0, true);
111 if (m_frameBufferCache.isEmpty())
112 m_frameBufferCache.resize(m_dirEntries.size());
113 // CAUTION: We must not resize m_frameBufferCache again after this, as
114 // decodeAtIndex() may give a BMPImageReader a pointer to one of the
115 // entries.
116 return m_frameBufferCache.size();
117 }
118
frameBufferAtIndex(size_t index)119 RGBA32Buffer* ICOImageDecoder::frameBufferAtIndex(size_t index)
120 {
121 // Ensure |index| is valid.
122 if (index >= frameCount())
123 return 0;
124
125 // Determine the image type, and if this is a BMP, decode.
126 decodeWithCheckForDataEnded(index, false);
127
128 // PNGs decode into their own framebuffers, so only use our internal cache
129 // for non-PNGs (BMP or unknown).
130 if (!m_pngDecoders[index])
131 return &m_frameBufferCache[index];
132
133 // Fail if the size the PNGImageDecoder calculated does not match the size
134 // in the directory.
135 if (m_pngDecoders[index]->isSizeAvailable()) {
136 const IntSize pngSize(m_pngDecoders[index]->size());
137 const IconDirectoryEntry& dirEntry = m_dirEntries[index];
138 if (pngSize != dirEntry.m_size) {
139 setFailed();
140 m_pngDecoders[index]->setFailed();
141 }
142 }
143
144 return m_pngDecoders[index]->frameBufferAtIndex(0);
145 }
146
147 // static
compareEntries(const IconDirectoryEntry & a,const IconDirectoryEntry & b)148 bool ICOImageDecoder::compareEntries(const IconDirectoryEntry& a,
149 const IconDirectoryEntry& b)
150 {
151 // Larger icons are better.
152 const int aEntryArea = a.m_size.width() * a.m_size.height();
153 const int bEntryArea = b.m_size.width() * b.m_size.height();
154 if (aEntryArea != bEntryArea)
155 return (aEntryArea > bEntryArea);
156
157 // Higher bit-depth icons are better.
158 return (a.m_bitCount > b.m_bitCount);
159 }
160
setDataForPNGDecoderAtIndex(size_t index)161 void ICOImageDecoder::setDataForPNGDecoderAtIndex(size_t index)
162 {
163 if (!m_pngDecoders[index])
164 return;
165
166 const IconDirectoryEntry& dirEntry = m_dirEntries[index];
167 // Copy out PNG data to a separate vector and send to the PNG decoder.
168 // FIXME: Save this copy by making the PNG decoder able to take an
169 // optional offset.
170 RefPtr<SharedBuffer> pngData(
171 SharedBuffer::create(&m_data->data()[dirEntry.m_imageOffset],
172 m_data->size() - dirEntry.m_imageOffset));
173 m_pngDecoders[index]->setData(pngData.get(), m_allDataReceived);
174 }
175
decodeWithCheckForDataEnded(size_t index,bool onlySize)176 void ICOImageDecoder::decodeWithCheckForDataEnded(size_t index, bool onlySize)
177 {
178 if (failed())
179 return;
180
181 // If we couldn't decode the image but we've received all the data, decoding
182 // has failed.
183 if ((!decodeDirectory() || (!onlySize && !decodeAtIndex(index)))
184 && m_allDataReceived)
185 setFailed();
186 }
187
decodeDirectory()188 bool ICOImageDecoder::decodeDirectory()
189 {
190 // Read and process directory.
191 if ((m_decodedOffset < sizeOfDirectory) && !processDirectory())
192 return false;
193
194 // Read and process directory entries.
195 return (m_decodedOffset >=
196 (sizeOfDirectory + (m_dirEntries.size() * sizeOfDirEntry)))
197 || processDirectoryEntries();
198 }
199
decodeAtIndex(size_t index)200 bool ICOImageDecoder::decodeAtIndex(size_t index)
201 {
202 ASSERT(index < m_dirEntries.size());
203 const IconDirectoryEntry& dirEntry = m_dirEntries[index];
204 if (!m_bmpReaders[index] && !m_pngDecoders[index]) {
205 // Image type unknown.
206 const ImageType imageType = imageTypeAtIndex(index);
207 if (imageType == BMP) {
208 // We need to have already sized m_frameBufferCache before this, and
209 // we must not resize it again later (see caution in frameCount()).
210 ASSERT(m_frameBufferCache.size() == m_dirEntries.size());
211 m_bmpReaders[index] =
212 new BMPImageReader(this, dirEntry.m_imageOffset, 0, true);
213 m_bmpReaders[index]->setData(m_data.get());
214 m_bmpReaders[index]->setBuffer(&m_frameBufferCache[index]);
215 } else if (imageType == PNG) {
216 m_pngDecoders[index] = new PNGImageDecoder();
217 setDataForPNGDecoderAtIndex(index);
218 } else {
219 // Not enough data to determine image type yet.
220 return false;
221 }
222 }
223
224 if (m_bmpReaders[index]) {
225 m_frameSize = dirEntry.m_size;
226 bool result = m_bmpReaders[index]->decodeBMP(false);
227 m_frameSize = IntSize();
228 return result;
229 }
230
231 // For PNGs, we're now done; further decoding will happen when calling
232 // frameBufferAtIndex() on the PNG decoder.
233 return true;
234 }
235
processDirectory()236 bool ICOImageDecoder::processDirectory()
237 {
238 // Read directory.
239 ASSERT(!m_decodedOffset);
240 if (m_data->size() < sizeOfDirectory)
241 return false;
242 const uint16_t fileType = readUint16(2);
243 const uint16_t idCount = readUint16(4);
244 m_decodedOffset = sizeOfDirectory;
245
246 // See if this is an icon filetype we understand, and make sure we have at
247 // least one entry in the directory.
248 enum {
249 ICON = 1,
250 CURSOR = 2,
251 };
252 if (((fileType != ICON) && (fileType != CURSOR)) || (idCount == 0)) {
253 setFailed();
254 return false;
255 }
256
257 // Enlarge member vectors to hold all the entries. We must initialize the
258 // BMP and PNG decoding vectors to 0 so that all entries can be safely
259 // deleted in our destructor. If we don't do this, they'll contain garbage
260 // values, and deleting those will corrupt memory.
261 m_dirEntries.resize(idCount);
262 m_bmpReaders.fill(0, idCount);
263 m_pngDecoders.fill(0, idCount);
264 return true;
265 }
266
processDirectoryEntries()267 bool ICOImageDecoder::processDirectoryEntries()
268 {
269 // Read directory entries.
270 ASSERT(m_decodedOffset == sizeOfDirectory);
271 if ((m_decodedOffset > m_data->size())
272 || ((m_data->size() - m_decodedOffset) <
273 (m_dirEntries.size() * sizeOfDirEntry)))
274 return false;
275 for (IconDirectoryEntries::iterator i(m_dirEntries.begin());
276 i != m_dirEntries.end(); ++i)
277 *i = readDirectoryEntry(); // Updates m_decodedOffset.
278
279 // Make sure the specified image offsets are past the end of the directory
280 // entries.
281 for (IconDirectoryEntries::iterator i(m_dirEntries.begin());
282 i != m_dirEntries.end(); ++i) {
283 if (i->m_imageOffset < m_decodedOffset) {
284 setFailed();
285 return false;
286 }
287 }
288
289 // Arrange frames in decreasing quality order.
290 std::sort(m_dirEntries.begin(), m_dirEntries.end(), compareEntries);
291
292 // The image size is the size of the largest entry.
293 const IconDirectoryEntry& dirEntry = m_dirEntries.first();
294 setSize(dirEntry.m_size.width(), dirEntry.m_size.height());
295 return true;
296 }
297
readDirectoryEntry()298 ICOImageDecoder::IconDirectoryEntry ICOImageDecoder::readDirectoryEntry()
299 {
300 // Read icon data.
301 // The casts to uint8_t in the next few lines are because that's the on-disk
302 // type of the width and height values. Storing them in ints (instead of
303 // matching uint8_ts) is so we can record dimensions of size 256 (which is
304 // what a zero byte really means).
305 int width = static_cast<uint8_t>(m_data->data()[m_decodedOffset]);
306 if (width == 0)
307 width = 256;
308 int height = static_cast<uint8_t>(m_data->data()[m_decodedOffset + 1]);
309 if (height == 0)
310 height = 256;
311 IconDirectoryEntry entry;
312 entry.m_size = IntSize(width, height);
313 entry.m_bitCount = readUint16(6);
314 entry.m_imageOffset = readUint32(12);
315
316 // Some icons don't have a bit depth, only a color count. Convert the
317 // color count to the minimum necessary bit depth. It doesn't matter if
318 // this isn't quite what the bitmap info header says later, as we only use
319 // this value to determine which icon entry is best.
320 if (!entry.m_bitCount) {
321 uint8_t colorCount = m_data->data()[m_decodedOffset + 2];
322 if (colorCount) {
323 for (--colorCount; colorCount; colorCount >>= 1)
324 ++entry.m_bitCount;
325 }
326 }
327
328 m_decodedOffset += sizeOfDirEntry;
329 return entry;
330 }
331
imageTypeAtIndex(size_t index)332 ICOImageDecoder::ImageType ICOImageDecoder::imageTypeAtIndex(size_t index)
333 {
334 // Check if this entry is a BMP or a PNG; we need 4 bytes to check the magic
335 // number.
336 ASSERT(index < m_dirEntries.size());
337 const uint32_t imageOffset = m_dirEntries[index].m_imageOffset;
338 if ((imageOffset > m_data->size()) || ((m_data->size() - imageOffset) < 4))
339 return Unknown;
340 return strncmp(&m_data->data()[imageOffset], "\x89PNG", 4) ? BMP : PNG;
341 }
342
343 }
344