|
1 /* vim:set tw=80 expandtab softtabstop=4 ts=4 sw=4: */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 /* I got the format description from http://www.daubnet.com/formats/BMP.html */ |
|
6 |
|
7 /* This is a Cross-Platform BMP Decoder, which should work everywhere, including |
|
8 * Big-Endian machines like the PowerPC. */ |
|
9 |
|
10 #include <stdlib.h> |
|
11 |
|
12 #include "ImageLogging.h" |
|
13 #include "mozilla/Endian.h" |
|
14 #include "nsBMPDecoder.h" |
|
15 |
|
16 #include "nsIInputStream.h" |
|
17 #include "RasterImage.h" |
|
18 #include <algorithm> |
|
19 |
|
20 namespace mozilla { |
|
21 namespace image { |
|
22 |
|
23 #ifdef PR_LOGGING |
|
24 static PRLogModuleInfo * |
|
25 GetBMPLog() |
|
26 { |
|
27 static PRLogModuleInfo *sBMPLog; |
|
28 if (!sBMPLog) |
|
29 sBMPLog = PR_NewLogModule("BMPDecoder"); |
|
30 return sBMPLog; |
|
31 } |
|
32 #endif |
|
33 |
|
34 // Convert from row (1..height) to absolute line (0..height-1) |
|
35 #define LINE(row) ((mBIH.height < 0) ? (-mBIH.height - (row)) : ((row) - 1)) |
|
36 #define PIXEL_OFFSET(row, col) (LINE(row) * mBIH.width + col) |
|
37 |
|
38 nsBMPDecoder::nsBMPDecoder(RasterImage &aImage) |
|
39 : Decoder(aImage) |
|
40 { |
|
41 mColors = nullptr; |
|
42 mRow = nullptr; |
|
43 mCurPos = mPos = mNumColors = mRowBytes = 0; |
|
44 mOldLine = mCurLine = 1; // Otherwise decoder will never start |
|
45 mState = eRLEStateInitial; |
|
46 mStateData = 0; |
|
47 mLOH = WIN_V3_HEADER_LENGTH; |
|
48 mUseAlphaData = mHaveAlphaData = false; |
|
49 } |
|
50 |
|
51 nsBMPDecoder::~nsBMPDecoder() |
|
52 { |
|
53 delete[] mColors; |
|
54 if (mRow) { |
|
55 moz_free(mRow); |
|
56 } |
|
57 } |
|
58 |
|
59 // Sets whether or not the BMP will use alpha data |
|
60 void |
|
61 nsBMPDecoder::SetUseAlphaData(bool useAlphaData) |
|
62 { |
|
63 mUseAlphaData = useAlphaData; |
|
64 } |
|
65 |
|
66 // Obtains the bits per pixel from the internal BIH header |
|
67 int32_t |
|
68 nsBMPDecoder::GetBitsPerPixel() const |
|
69 { |
|
70 return mBIH.bpp; |
|
71 } |
|
72 |
|
73 // Obtains the width from the internal BIH header |
|
74 int32_t |
|
75 nsBMPDecoder::GetWidth() const |
|
76 { |
|
77 return mBIH.width; |
|
78 } |
|
79 |
|
80 // Obtains the abs-value of the height from the internal BIH header |
|
81 int32_t |
|
82 nsBMPDecoder::GetHeight() const |
|
83 { |
|
84 return abs(mBIH.height); |
|
85 } |
|
86 |
|
87 // Obtains the internal output image buffer |
|
88 uint32_t* |
|
89 nsBMPDecoder::GetImageData() |
|
90 { |
|
91 return reinterpret_cast<uint32_t*>(mImageData); |
|
92 } |
|
93 |
|
94 // Obtains the size of the compressed image resource |
|
95 int32_t |
|
96 nsBMPDecoder::GetCompressedImageSize() const |
|
97 { |
|
98 // For everything except BI_RGB the header field must be defined |
|
99 if (mBIH.compression != BI_RGB) { |
|
100 return mBIH.image_size; |
|
101 } |
|
102 |
|
103 // mBIH.image_size isn't always filled for BI_RGB so calculate it manually |
|
104 // The pixel array size is calculated based on extra 4 byte boundary padding |
|
105 uint32_t rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to round up |
|
106 // Pad to DWORD Boundary |
|
107 if (rowSize % 4) { |
|
108 rowSize += (4 - (rowSize % 4)); |
|
109 } |
|
110 |
|
111 // The height should be the absolute value of what the height is in the BIH. |
|
112 // If positive the bitmap is stored bottom to top, otherwise top to bottom |
|
113 int32_t pixelArraySize = rowSize * GetHeight(); |
|
114 return pixelArraySize; |
|
115 } |
|
116 |
|
117 // Obtains whether or not a BMP file had alpha data in its 4th byte |
|
118 // for 32BPP bitmaps. Only use after the bitmap has been processed. |
|
119 bool |
|
120 nsBMPDecoder::HasAlphaData() const |
|
121 { |
|
122 return mHaveAlphaData; |
|
123 } |
|
124 |
|
125 |
|
126 void |
|
127 nsBMPDecoder::FinishInternal() |
|
128 { |
|
129 // We shouldn't be called in error cases |
|
130 NS_ABORT_IF_FALSE(!HasError(), "Can't call FinishInternal on error!"); |
|
131 |
|
132 // We should never make multiple frames |
|
133 NS_ABORT_IF_FALSE(GetFrameCount() <= 1, "Multiple BMP frames?"); |
|
134 |
|
135 // Send notifications if appropriate |
|
136 if (!IsSizeDecode() && HasSize()) { |
|
137 |
|
138 // Invalidate |
|
139 nsIntRect r(0, 0, mBIH.width, GetHeight()); |
|
140 PostInvalidation(r); |
|
141 |
|
142 if (mUseAlphaData) { |
|
143 PostFrameStop(FrameBlender::kFrameHasAlpha); |
|
144 } else { |
|
145 PostFrameStop(FrameBlender::kFrameOpaque); |
|
146 } |
|
147 PostDecodeDone(); |
|
148 } |
|
149 } |
|
150 |
|
151 // ---------------------------------------- |
|
152 // Actual Data Processing |
|
153 // ---------------------------------------- |
|
154 |
|
155 static void calcBitmask(uint32_t aMask, uint8_t& aBegin, uint8_t& aLength) |
|
156 { |
|
157 // find the rightmost 1 |
|
158 uint8_t pos; |
|
159 bool started = false; |
|
160 aBegin = aLength = 0; |
|
161 for (pos = 0; pos <= 31; pos++) { |
|
162 if (!started && (aMask & (1 << pos))) { |
|
163 aBegin = pos; |
|
164 started = true; |
|
165 } |
|
166 else if (started && !(aMask & (1 << pos))) { |
|
167 aLength = pos - aBegin; |
|
168 break; |
|
169 } |
|
170 } |
|
171 } |
|
172 |
|
173 NS_METHOD nsBMPDecoder::CalcBitShift() |
|
174 { |
|
175 uint8_t begin, length; |
|
176 // red |
|
177 calcBitmask(mBitFields.red, begin, length); |
|
178 mBitFields.redRightShift = begin; |
|
179 mBitFields.redLeftShift = 8 - length; |
|
180 // green |
|
181 calcBitmask(mBitFields.green, begin, length); |
|
182 mBitFields.greenRightShift = begin; |
|
183 mBitFields.greenLeftShift = 8 - length; |
|
184 // blue |
|
185 calcBitmask(mBitFields.blue, begin, length); |
|
186 mBitFields.blueRightShift = begin; |
|
187 mBitFields.blueLeftShift = 8 - length; |
|
188 return NS_OK; |
|
189 } |
|
190 |
|
191 void |
|
192 nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy) |
|
193 { |
|
194 NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!"); |
|
195 |
|
196 // aCount=0 means EOF, mCurLine=0 means we're past end of image |
|
197 if (!aCount || !mCurLine) |
|
198 return; |
|
199 |
|
200 if (mPos < BFH_INTERNAL_LENGTH) { /* In BITMAPFILEHEADER */ |
|
201 uint32_t toCopy = BFH_INTERNAL_LENGTH - mPos; |
|
202 if (toCopy > aCount) |
|
203 toCopy = aCount; |
|
204 memcpy(mRawBuf + mPos, aBuffer, toCopy); |
|
205 mPos += toCopy; |
|
206 aCount -= toCopy; |
|
207 aBuffer += toCopy; |
|
208 } |
|
209 if (mPos == BFH_INTERNAL_LENGTH) { |
|
210 ProcessFileHeader(); |
|
211 if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M') { |
|
212 PostDataError(); |
|
213 return; |
|
214 } |
|
215 if (mBFH.bihsize == OS2_BIH_LENGTH) |
|
216 mLOH = OS2_HEADER_LENGTH; |
|
217 } |
|
218 if (mPos >= BFH_INTERNAL_LENGTH && mPos < mLOH) { /* In BITMAPINFOHEADER */ |
|
219 uint32_t toCopy = mLOH - mPos; |
|
220 if (toCopy > aCount) |
|
221 toCopy = aCount; |
|
222 memcpy(mRawBuf + (mPos - BFH_INTERNAL_LENGTH), aBuffer, toCopy); |
|
223 mPos += toCopy; |
|
224 aCount -= toCopy; |
|
225 aBuffer += toCopy; |
|
226 } |
|
227 |
|
228 // HasSize is called to ensure that if at this point mPos == mLOH but |
|
229 // we have no data left to process, the next time WriteInternal is called |
|
230 // we won't enter this condition again. |
|
231 if (mPos == mLOH && !HasSize()) { |
|
232 ProcessInfoHeader(); |
|
233 PR_LOG(GetBMPLog(), PR_LOG_DEBUG, ("BMP is %lix%lix%lu. compression=%lu\n", |
|
234 mBIH.width, mBIH.height, mBIH.bpp, mBIH.compression)); |
|
235 // Verify we support this bit depth |
|
236 if (mBIH.bpp != 1 && mBIH.bpp != 4 && mBIH.bpp != 8 && |
|
237 mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32) { |
|
238 PostDataError(); |
|
239 return; |
|
240 } |
|
241 |
|
242 // BMPs with negative width are invalid |
|
243 // Reject extremely wide images to keep the math sane |
|
244 const int32_t k64KWidth = 0x0000FFFF; |
|
245 if (mBIH.width < 0 || mBIH.width > k64KWidth) { |
|
246 PostDataError(); |
|
247 return; |
|
248 } |
|
249 |
|
250 if (mBIH.height == INT_MIN) { |
|
251 PostDataError(); |
|
252 return; |
|
253 } |
|
254 |
|
255 uint32_t real_height = GetHeight(); |
|
256 |
|
257 // Post our size to the superclass |
|
258 PostSize(mBIH.width, real_height); |
|
259 if (HasError()) { |
|
260 // Setting the size led to an error. |
|
261 return; |
|
262 } |
|
263 |
|
264 // We have the size. If we're doing a size decode, we got what |
|
265 // we came for. |
|
266 if (IsSizeDecode()) |
|
267 return; |
|
268 |
|
269 // We're doing a real decode. |
|
270 mOldLine = mCurLine = real_height; |
|
271 |
|
272 if (mBIH.bpp <= 8) { |
|
273 mNumColors = 1 << mBIH.bpp; |
|
274 if (mBIH.colors && mBIH.colors < mNumColors) |
|
275 mNumColors = mBIH.colors; |
|
276 |
|
277 // Always allocate 256 even though mNumColors might be smaller |
|
278 mColors = new colorTable[256]; |
|
279 memset(mColors, 0, 256 * sizeof(colorTable)); |
|
280 } |
|
281 else if (mBIH.compression != BI_BITFIELDS && mBIH.bpp == 16) { |
|
282 // Use default 5-5-5 format |
|
283 mBitFields.red = 0x7C00; |
|
284 mBitFields.green = 0x03E0; |
|
285 mBitFields.blue = 0x001F; |
|
286 CalcBitShift(); |
|
287 } |
|
288 |
|
289 // Make sure we have a valid value for our supported compression modes |
|
290 // before adding the frame |
|
291 if (mBIH.compression != BI_RGB && mBIH.compression != BI_RLE8 && |
|
292 mBIH.compression != BI_RLE4 && mBIH.compression != BI_BITFIELDS) { |
|
293 PostDataError(); |
|
294 return; |
|
295 } |
|
296 |
|
297 // If we have RLE4 or RLE8 or BI_ALPHABITFIELDS, then ensure we |
|
298 // have valid BPP values before adding the frame |
|
299 if (mBIH.compression == BI_RLE8 && mBIH.bpp != 8) { |
|
300 PR_LOG(GetBMPLog(), PR_LOG_DEBUG, |
|
301 ("BMP RLE8 compression only supports 8 bits per pixel\n")); |
|
302 PostDataError(); |
|
303 return; |
|
304 } |
|
305 if (mBIH.compression == BI_RLE4 && mBIH.bpp != 4 && mBIH.bpp != 1) { |
|
306 PR_LOG(GetBMPLog(), PR_LOG_DEBUG, |
|
307 ("BMP RLE4 compression only supports 4 bits per pixel\n")); |
|
308 PostDataError(); |
|
309 return; |
|
310 } |
|
311 if (mBIH.compression == BI_ALPHABITFIELDS && |
|
312 mBIH.bpp != 16 && mBIH.bpp != 32) { |
|
313 PR_LOG(GetBMPLog(), PR_LOG_DEBUG, |
|
314 ("BMP ALPHABITFIELDS only supports 16 or 32 bits per pixel\n")); |
|
315 PostDataError(); |
|
316 return; |
|
317 } |
|
318 |
|
319 if (mBIH.compression != BI_RLE8 && mBIH.compression != BI_RLE4 && |
|
320 mBIH.compression != BI_ALPHABITFIELDS) { |
|
321 // mRow is not used for RLE encoded images |
|
322 mRow = (uint8_t*)moz_malloc((mBIH.width * mBIH.bpp) / 8 + 4); |
|
323 // + 4 because the line is padded to a 4 bit boundary, but I don't want |
|
324 // to make exact calculations here, that's unnecessary. |
|
325 // Also, it compensates rounding error. |
|
326 if (!mRow) { |
|
327 PostDataError(); |
|
328 return; |
|
329 } |
|
330 } |
|
331 if (!mImageData) { |
|
332 PostDecoderError(NS_ERROR_FAILURE); |
|
333 return; |
|
334 } |
|
335 |
|
336 // Prepare for transparency |
|
337 if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) { |
|
338 // Clear the image, as the RLE may jump over areas |
|
339 memset(mImageData, 0, mImageDataLength); |
|
340 } |
|
341 } |
|
342 |
|
343 if (mColors && mPos >= mLOH) { |
|
344 // OS/2 Bitmaps have no padding byte |
|
345 uint8_t bytesPerColor = (mBFH.bihsize == OS2_BIH_LENGTH) ? 3 : 4; |
|
346 if (mPos < (mLOH + mNumColors * bytesPerColor)) { |
|
347 // Number of bytes already received |
|
348 uint32_t colorBytes = mPos - mLOH; |
|
349 // Color which is currently received |
|
350 uint8_t colorNum = colorBytes / bytesPerColor; |
|
351 uint8_t at = colorBytes % bytesPerColor; |
|
352 while (aCount && (mPos < (mLOH + mNumColors * bytesPerColor))) { |
|
353 switch (at) { |
|
354 case 0: |
|
355 mColors[colorNum].blue = *aBuffer; |
|
356 break; |
|
357 case 1: |
|
358 mColors[colorNum].green = *aBuffer; |
|
359 break; |
|
360 case 2: |
|
361 mColors[colorNum].red = *aBuffer; |
|
362 // If there is no padding byte, increment the color index |
|
363 // since we're done with the current color. |
|
364 if (bytesPerColor == 3) |
|
365 colorNum++; |
|
366 break; |
|
367 case 3: |
|
368 // This is a padding byte only in Windows BMPs. Increment |
|
369 // the color index since we're done with the current color. |
|
370 colorNum++; |
|
371 break; |
|
372 } |
|
373 mPos++; aBuffer++; aCount--; |
|
374 at = (at + 1) % bytesPerColor; |
|
375 } |
|
376 } |
|
377 } |
|
378 else if (aCount && mBIH.compression == BI_BITFIELDS && mPos < (WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH)) { |
|
379 // If compression is used, this is a windows bitmap, hence we can |
|
380 // use WIN_HEADER_LENGTH instead of mLOH |
|
381 uint32_t toCopy = (WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH) - mPos; |
|
382 if (toCopy > aCount) |
|
383 toCopy = aCount; |
|
384 memcpy(mRawBuf + (mPos - WIN_V3_HEADER_LENGTH), aBuffer, toCopy); |
|
385 mPos += toCopy; |
|
386 aBuffer += toCopy; |
|
387 aCount -= toCopy; |
|
388 } |
|
389 if (mPos == WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH && |
|
390 mBIH.compression == BI_BITFIELDS) { |
|
391 mBitFields.red = LittleEndian::readUint32(reinterpret_cast<uint32_t*>(mRawBuf)); |
|
392 mBitFields.green = LittleEndian::readUint32(reinterpret_cast<uint32_t*>(mRawBuf + 4)); |
|
393 mBitFields.blue = LittleEndian::readUint32(reinterpret_cast<uint32_t*>(mRawBuf + 8)); |
|
394 CalcBitShift(); |
|
395 } |
|
396 while (aCount && (mPos < mBFH.dataoffset)) { // Skip whatever is between header and data |
|
397 mPos++; aBuffer++; aCount--; |
|
398 } |
|
399 if (aCount && ++mPos >= mBFH.dataoffset) { |
|
400 // Need to increment mPos, else we might get to mPos==mLOH again |
|
401 // From now on, mPos is irrelevant |
|
402 if (!mBIH.compression || mBIH.compression == BI_BITFIELDS) { |
|
403 uint32_t rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to round up |
|
404 if (rowSize % 4) { |
|
405 rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary |
|
406 } |
|
407 uint32_t toCopy; |
|
408 do { |
|
409 toCopy = rowSize - mRowBytes; |
|
410 if (toCopy) { |
|
411 if (toCopy > aCount) |
|
412 toCopy = aCount; |
|
413 memcpy(mRow + mRowBytes, aBuffer, toCopy); |
|
414 aCount -= toCopy; |
|
415 aBuffer += toCopy; |
|
416 mRowBytes += toCopy; |
|
417 } |
|
418 if (rowSize == mRowBytes) { |
|
419 // Collected a whole row into mRow, process it |
|
420 uint8_t* p = mRow; |
|
421 uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, 0); |
|
422 uint32_t lpos = mBIH.width; |
|
423 switch (mBIH.bpp) { |
|
424 case 1: |
|
425 while (lpos > 0) { |
|
426 int8_t bit; |
|
427 uint8_t idx; |
|
428 for (bit = 7; bit >= 0 && lpos > 0; bit--) { |
|
429 idx = (*p >> bit) & 1; |
|
430 SetPixel(d, idx, mColors); |
|
431 --lpos; |
|
432 } |
|
433 ++p; |
|
434 } |
|
435 break; |
|
436 case 4: |
|
437 while (lpos > 0) { |
|
438 Set4BitPixel(d, *p, lpos, mColors); |
|
439 ++p; |
|
440 } |
|
441 break; |
|
442 case 8: |
|
443 while (lpos > 0) { |
|
444 SetPixel(d, *p, mColors); |
|
445 --lpos; |
|
446 ++p; |
|
447 } |
|
448 break; |
|
449 case 16: |
|
450 while (lpos > 0) { |
|
451 uint16_t val = LittleEndian::readUint16(reinterpret_cast<uint16_t*>(p)); |
|
452 SetPixel(d, |
|
453 (val & mBitFields.red) >> mBitFields.redRightShift << mBitFields.redLeftShift, |
|
454 (val & mBitFields.green) >> mBitFields.greenRightShift << mBitFields.greenLeftShift, |
|
455 (val & mBitFields.blue) >> mBitFields.blueRightShift << mBitFields.blueLeftShift); |
|
456 --lpos; |
|
457 p+=2; |
|
458 } |
|
459 break; |
|
460 case 24: |
|
461 while (lpos > 0) { |
|
462 SetPixel(d, p[2], p[1], p[0]); |
|
463 p += 2; |
|
464 --lpos; |
|
465 ++p; |
|
466 } |
|
467 break; |
|
468 case 32: |
|
469 while (lpos > 0) { |
|
470 if (mUseAlphaData) { |
|
471 if (!mHaveAlphaData && p[3]) { |
|
472 // Non-zero alpha byte detected! Clear previous |
|
473 // pixels that we have already processed. |
|
474 // This works because we know that if we |
|
475 // are reaching here then the alpha data in byte |
|
476 // 4 has been right all along. And we know it |
|
477 // has been set to 0 the whole time, so that |
|
478 // means that everything is transparent so far. |
|
479 uint32_t* start = reinterpret_cast<uint32_t*>(mImageData) + GetWidth() * (mCurLine - 1); |
|
480 uint32_t heightDifference = GetHeight() - mCurLine + 1; |
|
481 uint32_t pixelCount = GetWidth() * heightDifference; |
|
482 |
|
483 memset(start, 0, pixelCount * sizeof(uint32_t)); |
|
484 |
|
485 mHaveAlphaData = true; |
|
486 } |
|
487 SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? p[3] : 0xFF); |
|
488 } else { |
|
489 SetPixel(d, p[2], p[1], p[0]); |
|
490 } |
|
491 p += 4; |
|
492 --lpos; |
|
493 } |
|
494 break; |
|
495 default: |
|
496 NS_NOTREACHED("Unsupported color depth, but earlier check didn't catch it"); |
|
497 } |
|
498 mCurLine --; |
|
499 if (mCurLine == 0) { // Finished last line |
|
500 break; |
|
501 } |
|
502 mRowBytes = 0; |
|
503 |
|
504 } |
|
505 } while (aCount > 0); |
|
506 } |
|
507 else if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) { |
|
508 if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) || |
|
509 ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) { |
|
510 PR_LOG(GetBMPLog(), PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n")); |
|
511 PostDataError(); |
|
512 return; |
|
513 } |
|
514 |
|
515 while (aCount > 0) { |
|
516 uint8_t byte; |
|
517 |
|
518 switch(mState) { |
|
519 case eRLEStateInitial: |
|
520 mStateData = (uint8_t)*aBuffer++; |
|
521 aCount--; |
|
522 |
|
523 mState = eRLEStateNeedSecondEscapeByte; |
|
524 continue; |
|
525 |
|
526 case eRLEStateNeedSecondEscapeByte: |
|
527 byte = *aBuffer++; |
|
528 aCount--; |
|
529 if (mStateData != RLE_ESCAPE) { // encoded mode |
|
530 // Encoded mode consists of two bytes: |
|
531 // the first byte (mStateData) specifies the |
|
532 // number of consecutive pixels to be drawn |
|
533 // using the color index contained in |
|
534 // the second byte |
|
535 // Work around bitmaps that specify too many pixels |
|
536 mState = eRLEStateInitial; |
|
537 uint32_t pixelsNeeded = std::min<uint32_t>(mBIH.width - mCurPos, mStateData); |
|
538 if (pixelsNeeded) { |
|
539 uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, mCurPos); |
|
540 mCurPos += pixelsNeeded; |
|
541 if (mBIH.compression == BI_RLE8) { |
|
542 do { |
|
543 SetPixel(d, byte, mColors); |
|
544 pixelsNeeded --; |
|
545 } while (pixelsNeeded); |
|
546 } else { |
|
547 do { |
|
548 Set4BitPixel(d, byte, pixelsNeeded, mColors); |
|
549 } while (pixelsNeeded); |
|
550 } |
|
551 } |
|
552 continue; |
|
553 } |
|
554 |
|
555 switch(byte) { |
|
556 case RLE_ESCAPE_EOL: |
|
557 // End of Line: Go to next row |
|
558 mCurLine --; |
|
559 mCurPos = 0; |
|
560 mState = eRLEStateInitial; |
|
561 break; |
|
562 |
|
563 case RLE_ESCAPE_EOF: // EndOfFile |
|
564 mCurPos = mCurLine = 0; |
|
565 break; |
|
566 |
|
567 case RLE_ESCAPE_DELTA: |
|
568 mState = eRLEStateNeedXDelta; |
|
569 continue; |
|
570 |
|
571 default : // absolute mode |
|
572 // Save the number of pixels to read |
|
573 mStateData = byte; |
|
574 if (mCurPos + mStateData > (uint32_t)mBIH.width) { |
|
575 // We can work around bitmaps that specify one |
|
576 // pixel too many, but only if their width is odd. |
|
577 mStateData -= mBIH.width & 1; |
|
578 if (mCurPos + mStateData > (uint32_t)mBIH.width) { |
|
579 PostDataError(); |
|
580 return; |
|
581 } |
|
582 } |
|
583 |
|
584 // See if we will need to skip a byte |
|
585 // to word align the pixel data |
|
586 // mStateData is a number of pixels |
|
587 // so allow for the RLE compression type |
|
588 // Pixels RLE8=1 RLE4=2 |
|
589 // 1 Pad Pad |
|
590 // 2 No Pad |
|
591 // 3 Pad No |
|
592 // 4 No No |
|
593 if (((mStateData - 1) & mBIH.compression) != 0) |
|
594 mState = eRLEStateAbsoluteMode; |
|
595 else |
|
596 mState = eRLEStateAbsoluteModePadded; |
|
597 continue; |
|
598 } |
|
599 break; |
|
600 |
|
601 case eRLEStateNeedXDelta: |
|
602 // Handle the XDelta and proceed to get Y Delta |
|
603 byte = *aBuffer++; |
|
604 aCount--; |
|
605 mCurPos += byte; |
|
606 if (mCurPos > mBIH.width) |
|
607 mCurPos = mBIH.width; |
|
608 |
|
609 mState = eRLEStateNeedYDelta; |
|
610 continue; |
|
611 |
|
612 case eRLEStateNeedYDelta: |
|
613 // Get the Y Delta and then "handle" the move |
|
614 byte = *aBuffer++; |
|
615 aCount--; |
|
616 mState = eRLEStateInitial; |
|
617 mCurLine -= std::min<int32_t>(byte, mCurLine); |
|
618 break; |
|
619 |
|
620 case eRLEStateAbsoluteMode: // Absolute Mode |
|
621 case eRLEStateAbsoluteModePadded: |
|
622 if (mStateData) { |
|
623 // In absolute mode, the second byte (mStateData) |
|
624 // represents the number of pixels |
|
625 // that follow, each of which contains |
|
626 // the color index of a single pixel. |
|
627 uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, mCurPos); |
|
628 uint32_t* oldPos = d; |
|
629 if (mBIH.compression == BI_RLE8) { |
|
630 while (aCount > 0 && mStateData > 0) { |
|
631 byte = *aBuffer++; |
|
632 aCount--; |
|
633 SetPixel(d, byte, mColors); |
|
634 mStateData--; |
|
635 } |
|
636 } else { |
|
637 while (aCount > 0 && mStateData > 0) { |
|
638 byte = *aBuffer++; |
|
639 aCount--; |
|
640 Set4BitPixel(d, byte, mStateData, mColors); |
|
641 } |
|
642 } |
|
643 mCurPos += d - oldPos; |
|
644 } |
|
645 |
|
646 if (mStateData == 0) { |
|
647 // In absolute mode, each run must |
|
648 // be aligned on a word boundary |
|
649 |
|
650 if (mState == eRLEStateAbsoluteMode) { // Word Aligned |
|
651 mState = eRLEStateInitial; |
|
652 } else if (aCount > 0) { // Not word Aligned |
|
653 // "next" byte is just a padding byte |
|
654 // so "move" past it and we can continue |
|
655 aBuffer++; |
|
656 aCount--; |
|
657 mState = eRLEStateInitial; |
|
658 } |
|
659 } |
|
660 // else state is still eRLEStateAbsoluteMode |
|
661 continue; |
|
662 |
|
663 default : |
|
664 NS_ABORT_IF_FALSE(0, "BMP RLE decompression: unknown state!"); |
|
665 PostDecoderError(NS_ERROR_UNEXPECTED); |
|
666 return; |
|
667 } |
|
668 // Because of the use of the continue statement |
|
669 // we only get here for eol, eof or y delta |
|
670 if (mCurLine == 0) { // Finished last line |
|
671 break; |
|
672 } |
|
673 } |
|
674 } |
|
675 } |
|
676 |
|
677 const uint32_t rows = mOldLine - mCurLine; |
|
678 if (rows) { |
|
679 |
|
680 // Invalidate |
|
681 nsIntRect r(0, mBIH.height < 0 ? -mBIH.height - mOldLine : mCurLine, |
|
682 mBIH.width, rows); |
|
683 PostInvalidation(r); |
|
684 |
|
685 mOldLine = mCurLine; |
|
686 } |
|
687 |
|
688 return; |
|
689 } |
|
690 |
|
691 void nsBMPDecoder::ProcessFileHeader() |
|
692 { |
|
693 memset(&mBFH, 0, sizeof(mBFH)); |
|
694 memcpy(&mBFH.signature, mRawBuf, sizeof(mBFH.signature)); |
|
695 memcpy(&mBFH.filesize, mRawBuf + 2, sizeof(mBFH.filesize)); |
|
696 memcpy(&mBFH.reserved, mRawBuf + 6, sizeof(mBFH.reserved)); |
|
697 memcpy(&mBFH.dataoffset, mRawBuf + 10, sizeof(mBFH.dataoffset)); |
|
698 memcpy(&mBFH.bihsize, mRawBuf + 14, sizeof(mBFH.bihsize)); |
|
699 |
|
700 // Now correct the endianness of the header |
|
701 mBFH.filesize = LittleEndian::readUint32(&mBFH.filesize); |
|
702 mBFH.dataoffset = LittleEndian::readUint32(&mBFH.dataoffset); |
|
703 mBFH.bihsize = LittleEndian::readUint32(&mBFH.bihsize); |
|
704 } |
|
705 |
|
706 void nsBMPDecoder::ProcessInfoHeader() |
|
707 { |
|
708 memset(&mBIH, 0, sizeof(mBIH)); |
|
709 if (mBFH.bihsize == 12) { // OS/2 Bitmap |
|
710 memcpy(&mBIH.width, mRawBuf, 2); |
|
711 memcpy(&mBIH.height, mRawBuf + 2, 2); |
|
712 memcpy(&mBIH.planes, mRawBuf + 4, sizeof(mBIH.planes)); |
|
713 memcpy(&mBIH.bpp, mRawBuf + 6, sizeof(mBIH.bpp)); |
|
714 } |
|
715 else { |
|
716 memcpy(&mBIH.width, mRawBuf, sizeof(mBIH.width)); |
|
717 memcpy(&mBIH.height, mRawBuf + 4, sizeof(mBIH.height)); |
|
718 memcpy(&mBIH.planes, mRawBuf + 8, sizeof(mBIH.planes)); |
|
719 memcpy(&mBIH.bpp, mRawBuf + 10, sizeof(mBIH.bpp)); |
|
720 memcpy(&mBIH.compression, mRawBuf + 12, sizeof(mBIH.compression)); |
|
721 memcpy(&mBIH.image_size, mRawBuf + 16, sizeof(mBIH.image_size)); |
|
722 memcpy(&mBIH.xppm, mRawBuf + 20, sizeof(mBIH.xppm)); |
|
723 memcpy(&mBIH.yppm, mRawBuf + 24, sizeof(mBIH.yppm)); |
|
724 memcpy(&mBIH.colors, mRawBuf + 28, sizeof(mBIH.colors)); |
|
725 memcpy(&mBIH.important_colors, mRawBuf + 32, sizeof(mBIH.important_colors)); |
|
726 } |
|
727 |
|
728 // Convert endianness |
|
729 mBIH.width = LittleEndian::readUint32(&mBIH.width); |
|
730 mBIH.height = LittleEndian::readUint32(&mBIH.height); |
|
731 mBIH.planes = LittleEndian::readUint16(&mBIH.planes); |
|
732 mBIH.bpp = LittleEndian::readUint16(&mBIH.bpp); |
|
733 |
|
734 mBIH.compression = LittleEndian::readUint32(&mBIH.compression); |
|
735 mBIH.image_size = LittleEndian::readUint32(&mBIH.image_size); |
|
736 mBIH.xppm = LittleEndian::readUint32(&mBIH.xppm); |
|
737 mBIH.yppm = LittleEndian::readUint32(&mBIH.yppm); |
|
738 mBIH.colors = LittleEndian::readUint32(&mBIH.colors); |
|
739 mBIH.important_colors = LittleEndian::readUint32(&mBIH.important_colors); |
|
740 } |
|
741 |
|
742 } // namespace image |
|
743 } // namespace mozilla |