• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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