Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 |