1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsWBMPDecoder.h"
8 #include "RasterImage.h"
11 #include "gfxPlatform.h"
18 static inline void SetPixel(uint32_t*& aDecoded
, bool aPixelWhite
)
20 uint8_t pixelValue
= aPixelWhite
? 255 : 0;
22 *aDecoded
++ = gfxPackedPixel(0xFF, pixelValue
, pixelValue
, pixelValue
);
25 /** Parses a WBMP encoded int field. Returns IntParseInProgress (out of
26 * data), IntParseSucceeded if the field was read OK or IntParseFailed
28 * The encoding used for WBMP ints is per byte. The high bit is a
29 * continuation flag saying (when set) that the next byte is part of the
30 * field, and the low seven bits are data. New data bits are added in the
31 * low bit positions, i.e. the field is big-endian (ignoring the high bits).
32 * @param aField Variable holds current value of field. When this function
33 * returns IntParseInProgress, aField will hold the
34 * intermediate result of the decoding, so this function can be
35 * called repeatedly for new bytes on the same field and will
37 * @param aBuffer Points to encoded field data.
38 * @param aCount Number of bytes in aBuffer. */
39 static WbmpIntDecodeStatus
DecodeEncodedInt (uint32_t& aField
, const char*& aBuffer
, uint32_t& aCount
)
42 // Check if the result would overflow if another seven bits were added.
43 // The actual test performed is AND to check if any of the top seven bits are set.
44 if (aField
& 0xFE000000) {
46 return IntParseFailed
;
49 // Get next encoded byte.
50 char encodedByte
= *aBuffer
;
52 // Update buffer state variables now we have read this byte.
56 // Work out and store the new (valid) value of the encoded int with this byte added.
57 aField
= (aField
<< 7) + (uint32_t)(encodedByte
& 0x7F);
59 if (!(encodedByte
& 0x80)) {
60 // No more bytes, value is complete.
61 return IntParseSucceeded
;
65 // Out of data but in the middle of an encoded int.
66 return IntParseInProgress
;
69 nsWBMPDecoder::nsWBMPDecoder(RasterImage
&aImage
)
76 mState(WbmpStateStart
)
81 nsWBMPDecoder::~nsWBMPDecoder()
87 nsWBMPDecoder::WriteInternal(const char *aBuffer
, uint32_t aCount
)
89 NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
91 // Loop until the input data is gone
96 // Since we only accept a type 0 WBMP we can just check the first byte is 0.
97 // (The specification says a well defined type 0 bitmap will start with a 0x00 byte).
98 if (*aBuffer
++ == 0x00) {
99 // This is a type 0 WBMP.
101 mState
= DecodingFixHeader
;
103 // This is a new type of WBMP or a type 0 WBMP defined oddly (e.g. 0x80 0x00)
105 mState
= DecodingFailed
;
111 case DecodingFixHeader
:
113 if ((*aBuffer
++ & 0x9F) == 0x00) {
114 // Fix header field is as expected
116 // For now, we skip the ext header field as it is not in a well-defined type 0 WBMP.
117 mState
= DecodingWidth
;
119 // Can't handle this fix header field.
121 mState
= DecodingFailed
;
129 WbmpIntDecodeStatus widthReadResult
= DecodeEncodedInt (mWidth
, aBuffer
, aCount
);
131 if (widthReadResult
== IntParseSucceeded
) {
132 mState
= DecodingHeight
;
133 } else if (widthReadResult
== IntParseFailed
) {
134 // Encoded width was bigger than a uint32_t or equal to 0.
136 mState
= DecodingFailed
;
139 // We are still parsing the encoded int field.
140 NS_ABORT_IF_FALSE((widthReadResult
== IntParseInProgress
),
141 "nsWBMPDecoder got bad result from an encoded width field");
149 WbmpIntDecodeStatus heightReadResult
= DecodeEncodedInt (mHeight
, aBuffer
, aCount
);
151 if (heightReadResult
== IntParseSucceeded
) {
152 // The header has now been entirely read.
153 const uint32_t k64KWidth
= 0x0000FFFF;
154 if (mWidth
== 0 || mWidth
> k64KWidth
155 || mHeight
== 0 || mHeight
> k64KWidth
) {
156 // consider 0 as an incorrect image size
157 // reject the extremely wide/high images to keep the math sane
159 mState
= DecodingFailed
;
163 // Post our size to the superclass
164 PostSize(mWidth
, mHeight
);
166 // Setting the size led to an error.
167 mState
= DecodingFailed
;
171 // If We're doing a size decode, we're done
172 if (IsSizeDecode()) {
173 mState
= WbmpStateFinished
;
178 PostDecoderError(NS_ERROR_FAILURE
);
179 mState
= DecodingFailed
;
183 // Create mRow, the buffer that holds one line of the raw image data
184 mRow
= (uint8_t*)moz_malloc((mWidth
+ 7) / 8);
186 PostDecoderError(NS_ERROR_OUT_OF_MEMORY
);
187 mState
= DecodingFailed
;
191 mState
= DecodingImageData
;
193 } else if (heightReadResult
== IntParseFailed
) {
194 // Encoded height was bigger than a uint32_t.
196 mState
= DecodingFailed
;
199 // We are still parsing the encoded int field.
200 NS_ABORT_IF_FALSE((heightReadResult
== IntParseInProgress
),
201 "nsWBMPDecoder got bad result from an encoded height field");
207 case DecodingImageData
:
209 uint32_t rowSize
= (mWidth
+ 7) / 8; // +7 to round up to nearest byte
210 uint32_t top
= mCurLine
;
212 // Process up to one row of data at a time until there is no more data.
213 while ((aCount
> 0) && (mCurLine
< mHeight
)) {
214 // Calculate if we need to copy data to fill the next buffered row of raw data.
215 uint32_t toCopy
= rowSize
- mRowBytes
;
217 // If required, copy raw data to fill a buffered row of raw data.
221 memcpy(mRow
+ mRowBytes
, aBuffer
, toCopy
);
227 // If there is a filled buffered row of raw data, process the row.
228 if (rowSize
== mRowBytes
) {
230 uint32_t *d
= reinterpret_cast<uint32_t*>(mImageData
) + (mWidth
* mCurLine
); // position of the first pixel at mCurLine
233 while (lpos
< mWidth
) {
234 for (int8_t bit
= 7; bit
>= 0; bit
--) {
237 bool pixelWhite
= (*p
>> bit
) & 1;
238 SetPixel(d
, pixelWhite
);
249 nsIntRect
r(0, top
, mWidth
, mCurLine
- top
);
253 // If we've got all the pixel bytes, we're finished
254 if (mCurLine
== mHeight
) {
257 mState
= WbmpStateFinished
;
262 case WbmpStateFinished
:
264 // Consume all excess data silently
271 NS_ABORT_IF_FALSE(0, "Shouldn't process any data after decode failed!");
279 } // namespace mozilla