image/decoders/nsBMPDecoder.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial