Bumping manifests a=b2g-bump
[gecko.git] / image / decoders / nsBMPDecoder.cpp
blob35b0db920772b698d0e4e1106979c46c172d0c8d
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/. */
6 // I got the format description from http://www.daubnet.com/formats/BMP.html
8 // This is a Cross-Platform BMP Decoder, which should work everywhere, including
9 // Big-Endian machines like the PowerPC.
11 #include <stdlib.h>
13 #include "ImageLogging.h"
14 #include "mozilla/Endian.h"
15 #include "mozilla/Likely.h"
16 #include "nsBMPDecoder.h"
18 #include "nsIInputStream.h"
19 #include "RasterImage.h"
20 #include <algorithm>
22 namespace mozilla {
23 namespace image {
25 #ifdef PR_LOGGING
26 static PRLogModuleInfo*
27 GetBMPLog()
29 static PRLogModuleInfo* sBMPLog;
30 if (!sBMPLog) {
31 sBMPLog = PR_NewLogModule("BMPDecoder");
33 return sBMPLog;
35 #endif
37 // Convert from row (1..height) to absolute line (0..height-1)
38 #define LINE(row) ((mBIH.height < 0) ? (-mBIH.height - (row)) : ((row) - 1))
39 #define PIXEL_OFFSET(row, col) (LINE(row) * mBIH.width + col)
41 nsBMPDecoder::nsBMPDecoder(RasterImage* aImage)
42 : Decoder(aImage)
43 , mPos(0)
44 , mLOH(WIN_V3_HEADER_LENGTH)
45 , mNumColors(0)
46 , mColors(nullptr)
47 , mRow(nullptr)
48 , mRowBytes(0)
49 , mCurLine(1) // Otherwise decoder will never start.
50 , mOldLine(1)
51 , mCurPos(0)
52 , mState(eRLEStateInitial)
53 , mStateData(0)
54 , mProcessedHeader(false)
55 , mUseAlphaData(false)
56 , mHaveAlphaData(false)
57 { }
59 nsBMPDecoder::~nsBMPDecoder()
61 delete[] mColors;
62 if (mRow) {
63 moz_free(mRow);
67 // Sets whether or not the BMP will use alpha data
68 void
69 nsBMPDecoder::SetUseAlphaData(bool useAlphaData)
71 mUseAlphaData = useAlphaData;
74 // Obtains the bits per pixel from the internal BIH header
75 int32_t
76 nsBMPDecoder::GetBitsPerPixel() const
78 return mBIH.bpp;
81 // Obtains the width from the internal BIH header
82 int32_t
83 nsBMPDecoder::GetWidth() const
85 return mBIH.width;
88 // Obtains the abs-value of the height from the internal BIH header
89 int32_t
90 nsBMPDecoder::GetHeight() const
92 return abs(mBIH.height);
95 // Obtains the internal output image buffer
96 uint32_t*
97 nsBMPDecoder::GetImageData()
99 return reinterpret_cast<uint32_t*>(mImageData);
102 // Obtains the size of the compressed image resource
103 int32_t
104 nsBMPDecoder::GetCompressedImageSize() const
106 // For everything except BI_RGB the header field must be defined
107 if (mBIH.compression != BI_RGB) {
108 return mBIH.image_size;
111 // mBIH.image_size isn't always filled for BI_RGB so calculate it manually
112 // The pixel array size is calculated based on extra 4 byte boundary padding
113 uint32_t rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to round up
115 // Pad to DWORD Boundary
116 if (rowSize % 4) {
117 rowSize += (4 - (rowSize % 4));
120 // The height should be the absolute value of what the height is in the BIH.
121 // If positive the bitmap is stored bottom to top, otherwise top to bottom
122 int32_t pixelArraySize = rowSize * GetHeight();
123 return pixelArraySize;
126 // Obtains whether or not a BMP file had alpha data in its 4th byte
127 // for 32BPP bitmaps. Only use after the bitmap has been processed.
128 bool
129 nsBMPDecoder::HasAlphaData() const
131 return mHaveAlphaData;
135 void
136 nsBMPDecoder::FinishInternal()
138 // We shouldn't be called in error cases
139 NS_ABORT_IF_FALSE(!HasError(), "Can't call FinishInternal on error!");
141 // We should never make multiple frames
142 NS_ABORT_IF_FALSE(GetFrameCount() <= 1, "Multiple BMP frames?");
144 // Send notifications if appropriate
145 if (!IsSizeDecode() && HasSize()) {
147 // Invalidate
148 nsIntRect r(0, 0, mBIH.width, GetHeight());
149 PostInvalidation(r);
151 if (mUseAlphaData) {
152 PostFrameStop(Opacity::SOME_TRANSPARENCY);
153 } else {
154 PostFrameStop(Opacity::OPAQUE);
156 PostDecodeDone();
160 // ----------------------------------------
161 // Actual Data Processing
162 // ----------------------------------------
164 static void
165 calcBitmask(uint32_t aMask, uint8_t& aBegin, uint8_t& aLength)
167 // find the rightmost 1
168 uint8_t pos;
169 bool started = false;
170 aBegin = aLength = 0;
171 for (pos = 0; pos <= 31; pos++) {
172 if (!started && (aMask & (1 << pos))) {
173 aBegin = pos;
174 started = true;
175 } else if (started && !(aMask & (1 << pos))) {
176 aLength = pos - aBegin;
177 break;
182 NS_METHOD
183 nsBMPDecoder::CalcBitShift()
185 uint8_t begin, length;
186 // red
187 calcBitmask(mBitFields.red, begin, length);
188 mBitFields.redRightShift = begin;
189 mBitFields.redLeftShift = 8 - length;
190 // green
191 calcBitmask(mBitFields.green, begin, length);
192 mBitFields.greenRightShift = begin;
193 mBitFields.greenLeftShift = 8 - length;
194 // blue
195 calcBitmask(mBitFields.blue, begin, length);
196 mBitFields.blueRightShift = begin;
197 mBitFields.blueLeftShift = 8 - length;
198 return NS_OK;
201 void
202 nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
204 NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
206 // aCount=0 means EOF, mCurLine=0 means we're past end of image
207 if (!aCount || !mCurLine) {
208 return;
211 // This code assumes that mRawBuf == WIN_V3_INTERNAL_BIH_LENGTH
212 // and that sizeof(mRawBuf) >= BFH_INTERNAL_LENGTH
213 MOZ_ASSERT(sizeof(mRawBuf) == WIN_V3_INTERNAL_BIH_LENGTH);
214 MOZ_ASSERT(sizeof(mRawBuf) >= BFH_INTERNAL_LENGTH);
215 MOZ_ASSERT(OS2_INTERNAL_BIH_LENGTH < WIN_V3_INTERNAL_BIH_LENGTH);
217 // This code also assumes it's working with a byte array
218 MOZ_ASSERT(sizeof(mRawBuf[0]) == 1);
220 if (mPos < BFH_INTERNAL_LENGTH) { /* In BITMAPFILEHEADER */
221 // BFH_INTERNAL_LENGTH < sizeof(mRawBuf)
222 // mPos < BFH_INTERNAL_LENGTH
223 // BFH_INTERNAL_LENGTH - mPos < sizeof(mRawBuf)
224 // so toCopy <= BFH_INTERNAL_LENGTH
225 // so toCopy < sizeof(mRawBuf)
226 // so toCopy > 0 && toCopy <= BFH_INTERNAL_LENGTH
227 uint32_t toCopy = BFH_INTERNAL_LENGTH - mPos;
228 if (toCopy > aCount) {
229 toCopy = aCount;
232 // mRawBuf is a byte array of size WIN_V3_INTERNAL_BIH_LENGTH
233 // (verified above)
234 // mPos is < BFH_INTERNAL_LENGTH
235 // BFH_INTERNAL_LENGTH < WIN_V3_INTERNAL_BIH_LENGTH
236 // so mPos < sizeof(mRawBuf)
238 // Therefore this assert should hold
239 MOZ_ASSERT(mPos < sizeof(mRawBuf));
241 // toCopy <= BFH_INTERNAL_LENGTH
242 // mPos >= 0 && mPos < BFH_INTERNAL_LENGTH
243 // sizeof(mRawBuf) >= BFH_INTERNAL_LENGTH (verified above)
245 // Therefore this assert should hold
246 MOZ_ASSERT(mPos + toCopy <= sizeof(mRawBuf));
248 memcpy(mRawBuf + mPos, aBuffer, toCopy);
249 mPos += toCopy;
250 aCount -= toCopy;
251 aBuffer += toCopy;
253 if (mPos == BFH_INTERNAL_LENGTH) {
254 ProcessFileHeader();
255 if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M') {
256 PostDataError();
257 return;
259 if (mBFH.bihsize == OS2_BIH_LENGTH) {
260 mLOH = OS2_HEADER_LENGTH;
263 if (mPos >= BFH_INTERNAL_LENGTH && mPos < mLOH) { /* In BITMAPINFOHEADER */
264 // mLOH == WIN_V3_HEADER_LENGTH || mLOH == OS2_HEADER_LENGTH
265 // OS2_HEADER_LENGTH < WIN_V3_HEADER_LENGTH
266 // BFH_INTERNAL_LENGTH < OS2_HEADER_LENGTH
267 // BFH_INTERNAL_LENGTH < WIN_V3_HEADER_LENGTH
269 // So toCopy is in the range
270 // 1 to (WIN_V3_HEADER_LENGTH - BFH_INTERNAL_LENGTH)
271 // or 1 to (OS2_HEADER_LENGTH - BFH_INTERNAL_LENGTH)
273 // But WIN_V3_HEADER_LENGTH =
274 // BFH_INTERNAL_LENGTH + WIN_V3_INTERNAL_BIH_LENGTH
275 // and OS2_HEADER_LENGTH = BFH_INTERNAL_LENGTH + OS2_INTERNAL_BIH_LENGTH
277 // So toCopy is in the range
279 // 1 to WIN_V3_INTERNAL_BIH_LENGTH
280 // or 1 to OS2_INTERNAL_BIH_LENGTH
281 // and OS2_INTERNAL_BIH_LENGTH < WIN_V3_INTERNAL_BIH_LENGTH
283 // sizeof(mRawBuf) = WIN_V3_INTERNAL_BIH_LENGTH
284 // so toCopy <= sizeof(mRawBuf)
285 uint32_t toCopy = mLOH - mPos;
286 if (toCopy > aCount) {
287 toCopy = aCount;
290 // mPos is in the range
291 // BFH_INTERNAL_LENGTH to (WIN_V3_HEADER_LENGTH - 1)
293 // offset is then in the range (see toCopy comments for more details)
294 // 0 to (WIN_V3_INTERNAL_BIH_LENGTH - 1)
296 // sizeof(mRawBuf) is WIN_V3_INTERNAL_BIH_LENGTH so this
297 // offset stays within bounds and this assert should hold
298 const uint32_t offset = mPos - BFH_INTERNAL_LENGTH;
299 MOZ_ASSERT(offset < sizeof(mRawBuf));
301 // Two cases:
302 // mPos = BFH_INTERNAL_LENGTH
303 // mLOH = WIN_V3_HEADER_LENGTH
305 // offset = 0
306 // toCopy = WIN_V3_INTERNAL_BIH_LENGTH
308 // This will be in the bounds of sizeof(mRawBuf)
310 // Second Case:
311 // mPos = WIN_V3_HEADER_LENGTH - 1
312 // mLOH = WIN_V3_HEADER_LENGTH
314 // offset = WIN_V3_INTERNAL_BIH_LENGTH - 1
315 // toCopy = 1
317 // This will be in the bounds of sizeof(mRawBuf)
319 // As sizeof(mRawBuf) == WIN_V3_INTERNAL_BIH_LENGTH (verified above)
320 // and WIN_V3_HEADER_LENGTH is the largest range of values. If mLOH
321 // was equal to OS2_HEADER_LENGTH then the ranges are smaller.
322 MOZ_ASSERT(offset + toCopy <= sizeof(mRawBuf));
324 memcpy(mRawBuf + offset, aBuffer, toCopy);
325 mPos += toCopy;
326 aCount -= toCopy;
327 aBuffer += toCopy;
330 // At this point mPos should be >= mLOH unless aBuffer did not have enough
331 // data. In the latter case aCount should be 0.
332 MOZ_ASSERT(mPos >= mLOH || aCount == 0);
334 // mProcessedHeader is checked to ensure that if at this point mPos == mLOH
335 // but we have no data left to process, the next time WriteInternal is called
336 // we won't enter this condition again.
337 if (mPos == mLOH && !mProcessedHeader) {
338 mProcessedHeader = true;
340 ProcessInfoHeader();
341 PR_LOG(GetBMPLog(), PR_LOG_DEBUG,
342 ("BMP is %lix%lix%lu. compression=%lu\n",
343 mBIH.width, mBIH.height, mBIH.bpp, mBIH.compression));
344 // Verify we support this bit depth
345 if (mBIH.bpp != 1 && mBIH.bpp != 4 && mBIH.bpp != 8 &&
346 mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32) {
347 PostDataError();
348 return;
351 // BMPs with negative width are invalid
352 // Reject extremely wide images to keep the math sane
353 const int32_t k64KWidth = 0x0000FFFF;
354 if (mBIH.width < 0 || mBIH.width > k64KWidth) {
355 PostDataError();
356 return;
359 if (mBIH.height == INT_MIN) {
360 PostDataError();
361 return;
364 uint32_t real_height = GetHeight();
366 // Post our size to the superclass
367 PostSize(mBIH.width, real_height);
368 if (HasError()) {
369 // Setting the size led to an error.
370 return;
373 // We have the size. If we're doing a size decode, we got what
374 // we came for.
375 if (IsSizeDecode()) {
376 return;
379 // We're doing a real decode.
380 mOldLine = mCurLine = real_height;
382 if (mBIH.bpp <= 8) {
383 mNumColors = 1 << mBIH.bpp;
384 if (mBIH.colors && mBIH.colors < mNumColors) {
385 mNumColors = mBIH.colors;
388 // Always allocate 256 even though mNumColors might be smaller
389 mColors = new colorTable[256];
390 memset(mColors, 0, 256 * sizeof(colorTable));
391 } else if (mBIH.compression != BI_BITFIELDS && mBIH.bpp == 16) {
392 // Use default 5-5-5 format
393 mBitFields.red = 0x7C00;
394 mBitFields.green = 0x03E0;
395 mBitFields.blue = 0x001F;
396 CalcBitShift();
399 // Make sure we have a valid value for our supported compression modes
400 // before adding the frame
401 if (mBIH.compression != BI_RGB && mBIH.compression != BI_RLE8 &&
402 mBIH.compression != BI_RLE4 && mBIH.compression != BI_BITFIELDS) {
403 PostDataError();
404 return;
407 // If we have RLE4 or RLE8 or BI_ALPHABITFIELDS, then ensure we
408 // have valid BPP values before adding the frame
409 if (mBIH.compression == BI_RLE8 && mBIH.bpp != 8) {
410 PR_LOG(GetBMPLog(), PR_LOG_DEBUG,
411 ("BMP RLE8 compression only supports 8 bits per pixel\n"));
412 PostDataError();
413 return;
415 if (mBIH.compression == BI_RLE4 && mBIH.bpp != 4 && mBIH.bpp != 1) {
416 PR_LOG(GetBMPLog(), PR_LOG_DEBUG,
417 ("BMP RLE4 compression only supports 4 bits per pixel\n"));
418 PostDataError();
419 return;
421 if (mBIH.compression == BI_ALPHABITFIELDS &&
422 mBIH.bpp != 16 && mBIH.bpp != 32) {
423 PR_LOG(GetBMPLog(), PR_LOG_DEBUG,
424 ("BMP ALPHABITFIELDS only supports 16 or 32 bits per pixel\n"
426 PostDataError();
427 return;
430 if (mBIH.compression != BI_RLE8 && mBIH.compression != BI_RLE4 &&
431 mBIH.compression != BI_ALPHABITFIELDS) {
432 // mRow is not used for RLE encoded images
433 mRow = (uint8_t*)moz_malloc((mBIH.width * mBIH.bpp) / 8 + 4);
434 // + 4 because the line is padded to a 4 bit boundary, but
435 // I don't want to make exact calculations here, that's unnecessary.
436 // Also, it compensates rounding error.
437 if (!mRow) {
438 PostDataError();
439 return;
442 if (!mImageData) {
443 PostDecoderError(NS_ERROR_FAILURE);
444 return;
447 // Prepare for transparency
448 if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
449 // Clear the image, as the RLE may jump over areas
450 memset(mImageData, 0, mImageDataLength);
454 if (mColors && mPos >= mLOH) {
455 // OS/2 Bitmaps have no padding byte
456 uint8_t bytesPerColor = (mBFH.bihsize == OS2_BIH_LENGTH) ? 3 : 4;
457 if (mPos < (mLOH + mNumColors * bytesPerColor)) {
458 // Number of bytes already received
459 uint32_t colorBytes = mPos - mLOH;
460 // Color which is currently received
461 uint8_t colorNum = colorBytes / bytesPerColor;
462 uint8_t at = colorBytes % bytesPerColor;
463 while (aCount && (mPos < (mLOH + mNumColors * bytesPerColor))) {
464 switch (at) {
465 case 0:
466 mColors[colorNum].blue = *aBuffer;
467 break;
468 case 1:
469 mColors[colorNum].green = *aBuffer;
470 break;
471 case 2:
472 mColors[colorNum].red = *aBuffer;
473 // If there is no padding byte, increment the color index
474 // since we're done with the current color.
475 if (bytesPerColor == 3) {
476 colorNum++;
478 break;
479 case 3:
480 // This is a padding byte only in Windows BMPs. Increment
481 // the color index since we're done with the current color.
482 colorNum++;
483 break;
485 mPos++; aBuffer++; aCount--;
486 at = (at + 1) % bytesPerColor;
489 } else if (aCount && mBIH.compression == BI_BITFIELDS && mPos <
490 (WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH)) {
491 // If compression is used, this is a windows bitmap (compression
492 // can't be used with OS/2 bitmaps),
493 // hence we can use WIN_V3_HEADER_LENGTH instead of mLOH.
494 // (verified below)
496 // If aCount != 0 then mPos should be >= mLOH due to the if statements
497 // at the beginning of the function
498 MOZ_ASSERT(mPos >= mLOH);
499 MOZ_ASSERT(mLOH == WIN_V3_HEADER_LENGTH);
501 // mLOH == WIN_V3_HEADER_LENGTH (verified above)
502 // mPos >= mLOH (verified above)
503 // mPos < WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH
505 // So toCopy is in the range
506 // 0 to (BITFIELD_LENGTH - 1)
507 uint32_t toCopy = (WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH) - mPos;
508 if (toCopy > aCount) {
509 toCopy = aCount;
512 // mPos >= WIN_V3_HEADER_LENGTH
513 // mPos < WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH
515 // offset is in the range
516 // 0 to (BITFIELD_LENGTH - 1)
518 // BITFIELD_LENGTH < WIN_V3_INTERNAL_BIH_LENGTH
519 // and sizeof(mRawBuf) == WIN_V3_INTERNAL_BIH_LENGTH (verified at
520 // top of function)
522 // Therefore this assert should hold
523 const uint32_t offset = mPos - WIN_V3_HEADER_LENGTH;
524 MOZ_ASSERT(offset < sizeof(mRawBuf));
526 // Two cases:
527 // mPos = WIN_V3_HEADER_LENGTH
529 // offset = 0
530 // toCopy = BITFIELD_LENGTH
532 // This will be in the bounds of sizeof(mRawBuf)
534 // Second case:
536 // mPos = WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH - 1
538 // offset = BITFIELD_LENGTH - 1
539 // toCopy = 1
541 // This will be in the bounds of sizeof(mRawBuf)
543 // As BITFIELD_LENGTH < WIN_V3_INTERNAL_BIH_LENGTH and
544 // sizeof(mRawBuf) == WIN_V3_INTERNAL_BIH_LENGTH
546 // Therefore this assert should hold
547 MOZ_ASSERT(offset + toCopy <= sizeof(mRawBuf));
549 memcpy(mRawBuf + offset, aBuffer, toCopy);
550 mPos += toCopy;
551 aBuffer += toCopy;
552 aCount -= toCopy;
554 if (mPos == WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH &&
555 mBIH.compression == BI_BITFIELDS) {
556 mBitFields.red = LittleEndian::readUint32(reinterpret_cast<uint32_t*>
557 (mRawBuf));
558 mBitFields.green = LittleEndian::readUint32(reinterpret_cast<uint32_t*>
559 (mRawBuf + 4));
560 mBitFields.blue = LittleEndian::readUint32(reinterpret_cast<uint32_t*>
561 (mRawBuf + 8));
562 CalcBitShift();
564 while (aCount && (mPos < mBFH.dataoffset)) { // Skip whatever is between
565 // header and data
566 mPos++; aBuffer++; aCount--;
568 if (aCount && ++mPos >= mBFH.dataoffset) {
569 // Need to increment mPos, else we might get to mPos==mLOH again
570 // From now on, mPos is irrelevant
571 if (!mBIH.compression || mBIH.compression == BI_BITFIELDS) {
572 uint32_t rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to
573 // round up
574 if (rowSize % 4) {
575 rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary
577 uint32_t toCopy;
578 do {
579 toCopy = rowSize - mRowBytes;
580 if (toCopy) {
581 if (toCopy > aCount) {
582 toCopy = aCount;
584 memcpy(mRow + mRowBytes, aBuffer, toCopy);
585 aCount -= toCopy;
586 aBuffer += toCopy;
587 mRowBytes += toCopy;
589 if (rowSize == mRowBytes) {
590 // Collected a whole row into mRow, process it
591 uint8_t* p = mRow;
592 uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) +
593 PIXEL_OFFSET(mCurLine, 0);
594 uint32_t lpos = mBIH.width;
595 switch (mBIH.bpp) {
596 case 1:
597 while (lpos > 0) {
598 int8_t bit;
599 uint8_t idx;
600 for (bit = 7; bit >= 0 && lpos > 0; bit--) {
601 idx = (*p >> bit) & 1;
602 SetPixel(d, idx, mColors);
603 --lpos;
605 ++p;
607 break;
608 case 4:
609 while (lpos > 0) {
610 Set4BitPixel(d, *p, lpos, mColors);
611 ++p;
613 break;
614 case 8:
615 while (lpos > 0) {
616 SetPixel(d, *p, mColors);
617 --lpos;
618 ++p;
620 break;
621 case 16:
622 while (lpos > 0) {
623 uint16_t val = LittleEndian::
624 readUint16(reinterpret_cast<uint16_t*>(p));
625 SetPixel(d,
626 (val & mBitFields.red) >>
627 mBitFields.redRightShift <<
628 mBitFields.redLeftShift,
629 (val & mBitFields.green) >>
630 mBitFields.greenRightShift <<
631 mBitFields.greenLeftShift,
632 (val & mBitFields.blue) >>
633 mBitFields.blueRightShift <<
634 mBitFields.blueLeftShift);
635 --lpos;
636 p+=2;
638 break;
639 case 24:
640 while (lpos > 0) {
641 SetPixel(d, p[2], p[1], p[0]);
642 p += 2;
643 --lpos;
644 ++p;
646 break;
647 case 32:
648 while (lpos > 0) {
649 if (mUseAlphaData) {
650 if (!mHaveAlphaData && p[3]) {
651 // Non-zero alpha byte detected! Clear previous
652 // pixels that we have already processed.
653 // This works because we know that if we
654 // are reaching here then the alpha data in byte
655 // 4 has been right all along. And we know it
656 // has been set to 0 the whole time, so that
657 // means that everything is transparent so far.
658 uint32_t* start = reinterpret_cast<uint32_t*>
659 (mImageData) + GetWidth() *
660 (mCurLine - 1);
661 uint32_t heightDifference = GetHeight() -
662 mCurLine + 1;
663 uint32_t pixelCount = GetWidth() *
664 heightDifference;
666 memset(start, 0, pixelCount * sizeof(uint32_t));
668 PostHasTransparency();
669 mHaveAlphaData = true;
671 SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? p[3] : 0xFF);
672 } else {
673 SetPixel(d, p[2], p[1], p[0]);
675 p += 4;
676 --lpos;
678 break;
679 default:
680 NS_NOTREACHED("Unsupported color depth,"
681 " but earlier check didn't catch it");
683 mCurLine --;
684 if (mCurLine == 0) { // Finished last line
685 break;
687 mRowBytes = 0;
689 } while (aCount > 0);
690 } else if ((mBIH.compression == BI_RLE8) ||
691 (mBIH.compression == BI_RLE4)) {
692 if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) ||
693 ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) &&
694 (mBIH.bpp != 1))) {
695 PR_LOG(GetBMPLog(), PR_LOG_DEBUG,
696 ("BMP RLE8/RLE4 compression only supports 8/4 bits per"
697 " pixel\n"));
698 PostDataError();
699 return;
702 while (aCount > 0) {
703 uint8_t byte;
705 switch(mState) {
706 case eRLEStateInitial:
707 mStateData = (uint8_t)*aBuffer++;
708 aCount--;
710 mState = eRLEStateNeedSecondEscapeByte;
711 continue;
713 case eRLEStateNeedSecondEscapeByte:
714 byte = *aBuffer++;
715 aCount--;
716 if (mStateData != RLE_ESCAPE) { // encoded mode
717 // Encoded mode consists of two bytes:
718 // the first byte (mStateData) specifies the
719 // number of consecutive pixels to be drawn
720 // using the color index contained in
721 // the second byte
722 // Work around bitmaps that specify too many pixels
723 mState = eRLEStateInitial;
724 uint32_t pixelsNeeded = std::min<uint32_t>(mBIH.width - mCurPos,
725 mStateData);
726 if (pixelsNeeded) {
727 uint32_t* d = reinterpret_cast<uint32_t*>
728 (mImageData) + PIXEL_OFFSET(mCurLine, mCurPos);
729 mCurPos += pixelsNeeded;
730 if (mBIH.compression == BI_RLE8) {
731 do {
732 SetPixel(d, byte, mColors);
733 pixelsNeeded --;
734 } while (pixelsNeeded);
735 } else {
736 do {
737 Set4BitPixel(d, byte, pixelsNeeded, mColors);
738 } while (pixelsNeeded);
741 continue;
744 switch(byte) {
745 case RLE_ESCAPE_EOL:
746 // End of Line: Go to next row
747 mCurLine --;
748 mCurPos = 0;
749 mState = eRLEStateInitial;
750 break;
752 case RLE_ESCAPE_EOF: // EndOfFile
753 mCurPos = mCurLine = 0;
754 break;
756 case RLE_ESCAPE_DELTA:
757 mState = eRLEStateNeedXDelta;
758 continue;
760 default : // absolute mode
761 // Save the number of pixels to read
762 mStateData = byte;
763 if (mCurPos + mStateData > (uint32_t)mBIH.width) {
764 // We can work around bitmaps that specify
765 // one pixel too many, but only if their
766 // width is odd.
767 mStateData -= mBIH.width & 1;
768 if (mCurPos + mStateData > (uint32_t)mBIH.width) {
769 PostDataError();
770 return;
774 // See if we will need to skip a byte
775 // to word align the pixel data
776 // mStateData is a number of pixels
777 // so allow for the RLE compression type
778 // Pixels RLE8=1 RLE4=2
779 // 1 Pad Pad
780 // 2 No Pad
781 // 3 Pad No
782 // 4 No No
783 if (((mStateData - 1) & mBIH.compression) != 0) {
784 mState = eRLEStateAbsoluteMode;
785 } else {
786 mState = eRLEStateAbsoluteModePadded;
788 continue;
790 break;
792 case eRLEStateNeedXDelta:
793 // Handle the XDelta and proceed to get Y Delta
794 byte = *aBuffer++;
795 aCount--;
796 mCurPos += byte;
797 // Delta encoding makes it possible to skip pixels
798 // making the image transparent.
799 if (MOZ_UNLIKELY(!mHaveAlphaData)) {
800 PostHasTransparency();
802 mUseAlphaData = mHaveAlphaData = true;
803 if (mCurPos > mBIH.width) {
804 mCurPos = mBIH.width;
807 mState = eRLEStateNeedYDelta;
808 continue;
810 case eRLEStateNeedYDelta:
811 // Get the Y Delta and then "handle" the move
812 byte = *aBuffer++;
813 aCount--;
814 mState = eRLEStateInitial;
815 // Delta encoding makes it possible to skip pixels
816 // making the image transparent.
817 if (MOZ_UNLIKELY(!mHaveAlphaData)) {
818 PostHasTransparency();
820 mUseAlphaData = mHaveAlphaData = true;
821 mCurLine -= std::min<int32_t>(byte, mCurLine);
822 break;
824 case eRLEStateAbsoluteMode: // Absolute Mode
825 case eRLEStateAbsoluteModePadded:
826 if (mStateData) {
827 // In absolute mode, the second byte (mStateData)
828 // represents the number of pixels
829 // that follow, each of which contains
830 // the color index of a single pixel.
831 uint32_t* d = reinterpret_cast<uint32_t*>
832 (mImageData) +
833 PIXEL_OFFSET(mCurLine, mCurPos);
834 uint32_t* oldPos = d;
835 if (mBIH.compression == BI_RLE8) {
836 while (aCount > 0 && mStateData > 0) {
837 byte = *aBuffer++;
838 aCount--;
839 SetPixel(d, byte, mColors);
840 mStateData--;
842 } else {
843 while (aCount > 0 && mStateData > 0) {
844 byte = *aBuffer++;
845 aCount--;
846 Set4BitPixel(d, byte, mStateData, mColors);
849 mCurPos += d - oldPos;
852 if (mStateData == 0) {
853 // In absolute mode, each run must
854 // be aligned on a word boundary
856 if (mState == eRLEStateAbsoluteMode) {
857 // word aligned
858 mState = eRLEStateInitial;
859 } else if (aCount > 0) {
860 // not word aligned
861 // "next" byte is just a padding byte
862 // so "move" past it and we can continue
863 aBuffer++;
864 aCount--;
865 mState = eRLEStateInitial;
868 // else state is still eRLEStateAbsoluteMode
869 continue;
871 default :
872 NS_ABORT_IF_FALSE(0,
873 "BMP RLE decompression: unknown state!");
874 PostDecoderError(NS_ERROR_UNEXPECTED);
875 return;
877 // Because of the use of the continue statement
878 // we only get here for eol, eof or y delta
879 if (mCurLine == 0) {
880 // Finished last line
881 break;
887 const uint32_t rows = mOldLine - mCurLine;
888 if (rows) {
889 // Invalidate
890 nsIntRect r(0, mBIH.height < 0 ? -mBIH.height - mOldLine : mCurLine,
891 mBIH.width, rows);
892 PostInvalidation(r);
894 mOldLine = mCurLine;
897 return;
900 void
901 nsBMPDecoder::ProcessFileHeader()
903 memset(&mBFH, 0, sizeof(mBFH));
904 memcpy(&mBFH.signature, mRawBuf, sizeof(mBFH.signature));
905 memcpy(&mBFH.filesize, mRawBuf + 2, sizeof(mBFH.filesize));
906 memcpy(&mBFH.reserved, mRawBuf + 6, sizeof(mBFH.reserved));
907 memcpy(&mBFH.dataoffset, mRawBuf + 10, sizeof(mBFH.dataoffset));
908 memcpy(&mBFH.bihsize, mRawBuf + 14, sizeof(mBFH.bihsize));
910 // Now correct the endianness of the header
911 mBFH.filesize = LittleEndian::readUint32(&mBFH.filesize);
912 mBFH.dataoffset = LittleEndian::readUint32(&mBFH.dataoffset);
913 mBFH.bihsize = LittleEndian::readUint32(&mBFH.bihsize);
916 void
917 nsBMPDecoder::ProcessInfoHeader()
919 memset(&mBIH, 0, sizeof(mBIH));
920 if (mBFH.bihsize == 12) { // OS/2 Bitmap
921 memcpy(&mBIH.width, mRawBuf, 2);
922 memcpy(&mBIH.height, mRawBuf + 2, 2);
923 memcpy(&mBIH.planes, mRawBuf + 4, sizeof(mBIH.planes));
924 memcpy(&mBIH.bpp, mRawBuf + 6, sizeof(mBIH.bpp));
925 } else {
926 memcpy(&mBIH.width, mRawBuf, sizeof(mBIH.width));
927 memcpy(&mBIH.height, mRawBuf + 4, sizeof(mBIH.height));
928 memcpy(&mBIH.planes, mRawBuf + 8, sizeof(mBIH.planes));
929 memcpy(&mBIH.bpp, mRawBuf + 10, sizeof(mBIH.bpp));
930 memcpy(&mBIH.compression, mRawBuf + 12, sizeof(mBIH.compression));
931 memcpy(&mBIH.image_size, mRawBuf + 16, sizeof(mBIH.image_size));
932 memcpy(&mBIH.xppm, mRawBuf + 20, sizeof(mBIH.xppm));
933 memcpy(&mBIH.yppm, mRawBuf + 24, sizeof(mBIH.yppm));
934 memcpy(&mBIH.colors, mRawBuf + 28, sizeof(mBIH.colors));
935 memcpy(&mBIH.important_colors, mRawBuf + 32,
936 sizeof(mBIH.important_colors));
939 // Convert endianness
940 mBIH.width = LittleEndian::readUint32(&mBIH.width);
941 mBIH.height = LittleEndian::readUint32(&mBIH.height);
942 mBIH.planes = LittleEndian::readUint16(&mBIH.planes);
943 mBIH.bpp = LittleEndian::readUint16(&mBIH.bpp);
945 mBIH.compression = LittleEndian::readUint32(&mBIH.compression);
946 mBIH.image_size = LittleEndian::readUint32(&mBIH.image_size);
947 mBIH.xppm = LittleEndian::readUint32(&mBIH.xppm);
948 mBIH.yppm = LittleEndian::readUint32(&mBIH.yppm);
949 mBIH.colors = LittleEndian::readUint32(&mBIH.colors);
950 mBIH.important_colors = LittleEndian::readUint32(&mBIH.important_colors);
953 } // namespace image
954 } // namespace mozilla