• 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 "XBMImageDecoder.h"
33 
34 #include "ASCIICType.h"
35 
36 #include <algorithm>
37 #include <cstdio>
38 
39 namespace WebCore {
40 
XBMImageDecoder()41 XBMImageDecoder::XBMImageDecoder()
42     : m_decodeOffset(0)
43     , m_allDataReceived(false)
44     , m_decodedHeader(false)
45     , m_dataType(Unknown)
46     , m_bitsDecoded(0)
47 {
48 }
49 
setData(SharedBuffer * data,bool allDataReceived)50 void XBMImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
51 {
52     ImageDecoder::setData(data, allDataReceived);
53     m_xbmString = data->buffer();
54     m_xbmString.append('\0');
55 
56     m_allDataReceived = allDataReceived;
57 }
58 
isSizeAvailable()59 bool XBMImageDecoder::isSizeAvailable()
60 {
61     if (!ImageDecoder::isSizeAvailable() && !m_failed)
62         decode(true);
63 
64     return ImageDecoder::isSizeAvailable();
65 }
66 
frameBufferAtIndex(size_t index)67 RGBA32Buffer* XBMImageDecoder::frameBufferAtIndex(size_t index)
68 {
69     if (index)
70         return 0;
71 
72     if (m_frameBufferCache.isEmpty())
73         m_frameBufferCache.resize(1);
74 
75     // Attempt to get the size if we don't have it yet.
76     if (!ImageDecoder::isSizeAvailable())
77         decode(true);
78 
79     // Initialize the framebuffer if needed.
80     RGBA32Buffer& buffer = m_frameBufferCache[0];
81     if (!failed() && ImageDecoder::isSizeAvailable()
82         && (buffer.status() == RGBA32Buffer::FrameEmpty)) {
83         if (!buffer.setSize(size().width(), size().height())) {
84             m_failed = true;
85             return 0;
86         }
87         buffer.setStatus(RGBA32Buffer::FramePartial);
88 
89         // For XBMs, the frame always fills the entire image.
90         buffer.setRect(IntRect(IntPoint(), size()));
91     }
92 
93     // Keep trying to decode until we've got the entire image.
94     if (buffer.status() == RGBA32Buffer::FramePartial)
95         decode(false);
96 
97     return &buffer;
98 }
99 
decodeHeader()100 bool XBMImageDecoder::decodeHeader()
101 {
102     ASSERT(m_decodeOffset <= m_xbmString.size());
103     ASSERT(!m_decodedHeader);
104 
105     const char* input = m_xbmString.data();
106 
107     // At least 2 "#define <string> <unsigned>" sequences are required. These
108     // specify the width and height of the image.
109     int width, height;
110     if (!ImageDecoder::isSizeAvailable()) {
111         int count;
112         if (sscanf(&input[m_decodeOffset], "#define %*s %i #define %*s %i%n",
113                    &width, &height, &count) != 2)
114             return false;
115 
116         // The width and height need to follow some rules.
117         if (width < 0 || width > maxDimension || height < 0 || height > maxDimension) {
118             // If this happens, decoding should not continue.
119             setFailed();
120             return false;
121         }
122 
123         if (!setSize(width, height)) {
124             setFailed();
125             return false;
126         }
127         m_decodeOffset += count;
128         ASSERT(m_decodeOffset <= m_xbmString.size());
129     }
130 
131     ASSERT(ImageDecoder::isSizeAvailable());
132 
133     // Now we're looking for something that tells us that we've seen all of the
134     // "#define <string> <unsigned>" sequences that we're going to. Mozilla
135     // just looks for " char " or " short ". We'll do the same.
136     if (m_dataType == Unknown) {
137         const char* x11hint = " char ";
138         const char* x11HintLocation = strstr(&input[m_decodeOffset], x11hint);
139         if (x11HintLocation) {
140             m_dataType = X11;
141             m_decodeOffset += ((x11HintLocation - &input[m_decodeOffset]) + strlen(x11hint));
142         } else {
143             const char* x10hint = " short ";
144             const char* x10HintLocation = strstr(&input[m_decodeOffset], x10hint);
145             if (x10HintLocation) {
146                 m_dataType = X10;
147                 m_decodeOffset += ((x10HintLocation - &input[m_decodeOffset]) + strlen(x10hint));
148             } else
149                 return false;
150         }
151         ASSERT(m_decodeOffset <= m_xbmString.size());
152     }
153 
154     // Find the start of the data. Again, we do what mozilla does and just
155     // look for a '{' in the input.
156     const char* found = strchr(&input[m_decodeOffset], '{');
157     if (!found)
158         return false;
159 
160     // Advance to character after the '{'
161     m_decodeOffset += ((found - &input[m_decodeOffset]) + 1);
162     ASSERT(m_decodeOffset <= m_xbmString.size());
163     m_decodedHeader = true;
164 
165     return true;
166 }
167 
168 // The data in an XBM file is provided as an array of either "char" or "short"
169 // values. These values are decoded one at a time using strtoul() and the bits
170 // are used to set the alpha value for the image.
171 //
172 // The value for the color is always set to RGB(0,0,0), the alpha value takes
173 // care of whether or not the pixel shows up.
174 //
175 // Since the data may arrive in chunks, and many prefixes of valid numbers are
176 // themselves valid numbers, this code needs to check to make sure that the
177 // value is not truncated. This is done by consuming space after the value
178 // read until a ',' or a '}' occurs. In a valid XBM, one of these characters
179 // will occur after each value.
180 //
181 // The checks after strtoul are based on Mozilla's nsXBMDecoder.cpp.
decodeDatum(uint16_t * result)182 bool XBMImageDecoder::decodeDatum(uint16_t* result)
183 {
184     const char* input = m_xbmString.data();
185     char* endPtr;
186     const uint16_t value = strtoul(&input[m_decodeOffset], &endPtr, 0);
187 
188     // End of input or couldn't decode anything, can't go any further.
189     if (endPtr == &input[m_decodeOffset] || !*endPtr)
190         return false;
191 
192     // Possibly a hex value truncated at "0x". Need more data.
193     if (value == 0 && (*endPtr == 'x' || *endPtr == 'X'))
194         return false;
195 
196     // Skip whitespace
197     while (*endPtr && isASCIISpace(*endPtr))
198         ++endPtr;
199 
200     // Out of input, don't know what comes next.
201     if (!*endPtr)
202         return false;
203 
204     // If the next non-whitespace character is not one of these, it's an error.
205     // Every valid entry in the data array needs to be followed by ',' or '}'.
206     if (*endPtr != ',' && *endPtr != '}') {
207         setFailed();
208         return false;
209     }
210 
211     // At this point we have a value.
212     *result = value;
213 
214     // Skip over the decoded value plus the delimiter (',' or '}').
215     m_decodeOffset += ((endPtr - &input[m_decodeOffset]) + 1);
216     ASSERT(m_decodeOffset <= m_xbmString.size());
217 
218     return true;
219 }
220 
decodeData()221 bool XBMImageDecoder::decodeData()
222 {
223     ASSERT(m_decodeOffset <= m_xbmString.size());
224     ASSERT(m_decodedHeader && !m_frameBufferCache.isEmpty());
225 
226     RGBA32Buffer& frame = m_frameBufferCache[0];
227 
228     ASSERT(frame.status() == RGBA32Buffer::FramePartial);
229 
230     const int bitsPerRow = size().width();
231 
232     ASSERT(m_dataType != Unknown);
233 
234     while (m_bitsDecoded < (size().width() * size().height())) {
235         uint16_t value;
236         if (!decodeDatum(&value))
237             return false;
238 
239         int x = m_bitsDecoded % bitsPerRow;
240         const int y = m_bitsDecoded / bitsPerRow;
241 
242         // How many bits will be written?
243         const int bits = std::min(bitsPerRow - x, (m_dataType == X11) ? 8 : 16);
244 
245         // Only the alpha channel matters here, so the color values are always
246         // set to 0.
247         for (int i = 0; i < bits; ++i)
248             frame.setRGBA(x++, y, 0, 0, 0, value & (1 << i) ? 255 : 0);
249 
250         m_bitsDecoded += bits;
251     }
252 
253     frame.setStatus(RGBA32Buffer::FrameComplete);
254 
255     return true;
256 }
257 
258 // Decode as much as we can of the XBM file.
decode(bool sizeOnly)259 void XBMImageDecoder::decode(bool sizeOnly)
260 {
261     if (failed())
262         return;
263 
264     bool decodeResult = false;
265 
266     if (!m_decodedHeader)
267         decodeResult = decodeHeader();
268 
269     if (m_decodedHeader && !sizeOnly)
270         decodeResult = decodeData();
271 
272     // The header or the data could not be decoded, but there is no more
273     // data: decoding has failed.
274     if (!decodeResult && m_allDataReceived)
275         setFailed();
276 }
277 
278 } // namespace WebCore
279