Bug 886173 - Preserve playbackRate across pause/play. r=cpearce
[gecko.git] / image / decoders / nsWBMPDecoder.cpp
blob801cc9bc212a9e9b09d1e7b70bddc1c8f9b86119
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"
9 #include "nspr.h"
10 #include "nsRect.h"
11 #include "gfxPlatform.h"
13 #include "nsError.h"
15 namespace mozilla {
16 namespace image {
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
27 * on an error.
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
36 * operate correctly.
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)
41 while (aCount > 0) {
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) {
45 // Overflow :(
46 return IntParseFailed;
49 // Get next encoded byte.
50 char encodedByte = *aBuffer;
52 // Update buffer state variables now we have read this byte.
53 aBuffer++;
54 aCount--;
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)
70 : Decoder(aImage),
71 mWidth(0),
72 mHeight(0),
73 mRow(nullptr),
74 mRowBytes(0),
75 mCurLine(0),
76 mState(WbmpStateStart)
78 // Nothing to do
81 nsWBMPDecoder::~nsWBMPDecoder()
83 moz_free(mRow);
86 void
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
92 while (aCount > 0) {
93 switch (mState) {
94 case WbmpStateStart:
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.
100 aCount--;
101 mState = DecodingFixHeader;
102 } else {
103 // This is a new type of WBMP or a type 0 WBMP defined oddly (e.g. 0x80 0x00)
104 PostDataError();
105 mState = DecodingFailed;
106 return;
108 break;
111 case DecodingFixHeader:
113 if ((*aBuffer++ & 0x9F) == 0x00) {
114 // Fix header field is as expected
115 aCount--;
116 // For now, we skip the ext header field as it is not in a well-defined type 0 WBMP.
117 mState = DecodingWidth;
118 } else {
119 // Can't handle this fix header field.
120 PostDataError();
121 mState = DecodingFailed;
122 return;
124 break;
127 case DecodingWidth:
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.
135 PostDataError();
136 mState = DecodingFailed;
137 return;
138 } else {
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");
142 return;
144 break;
147 case DecodingHeight:
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
158 PostDataError();
159 mState = DecodingFailed;
160 return;
163 // Post our size to the superclass
164 PostSize(mWidth, mHeight);
165 if (HasError()) {
166 // Setting the size led to an error.
167 mState = DecodingFailed;
168 return;
171 // If We're doing a size decode, we're done
172 if (IsSizeDecode()) {
173 mState = WbmpStateFinished;
174 return;
177 if (!mImageData) {
178 PostDecoderError(NS_ERROR_FAILURE);
179 mState = DecodingFailed;
180 return;
183 // Create mRow, the buffer that holds one line of the raw image data
184 mRow = (uint8_t*)moz_malloc((mWidth + 7) / 8);
185 if (!mRow) {
186 PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
187 mState = DecodingFailed;
188 return;
191 mState = DecodingImageData;
193 } else if (heightReadResult == IntParseFailed) {
194 // Encoded height was bigger than a uint32_t.
195 PostDataError();
196 mState = DecodingFailed;
197 return;
198 } else {
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");
202 return;
204 break;
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.
218 if (toCopy) {
219 if (toCopy > aCount)
220 toCopy = aCount;
221 memcpy(mRow + mRowBytes, aBuffer, toCopy);
222 aCount -= toCopy;
223 aBuffer += toCopy;
224 mRowBytes += toCopy;
227 // If there is a filled buffered row of raw data, process the row.
228 if (rowSize == mRowBytes) {
229 uint8_t *p = mRow;
230 uint32_t *d = reinterpret_cast<uint32_t*>(mImageData) + (mWidth * mCurLine); // position of the first pixel at mCurLine
231 uint32_t lpos = 0;
233 while (lpos < mWidth) {
234 for (int8_t bit = 7; bit >= 0; bit--) {
235 if (lpos >= mWidth)
236 break;
237 bool pixelWhite = (*p >> bit) & 1;
238 SetPixel(d, pixelWhite);
239 ++lpos;
241 ++p;
244 mCurLine++;
245 mRowBytes = 0;
249 nsIntRect r(0, top, mWidth, mCurLine - top);
250 // Invalidate
251 PostInvalidation(r);
253 // If we've got all the pixel bytes, we're finished
254 if (mCurLine == mHeight) {
255 PostFrameStop();
256 PostDecodeDone();
257 mState = WbmpStateFinished;
259 break;
262 case WbmpStateFinished:
264 // Consume all excess data silently
265 aCount = 0;
266 break;
269 case DecodingFailed:
271 NS_ABORT_IF_FALSE(0, "Shouldn't process any data after decode failed!");
272 return;
278 } // namespace image
279 } // namespace mozilla