Thu, 15 Jan 2015 15:59:08 +0100
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 | |
michael@0 | 6 | /* This is a Cross-Platform ICO Decoder, which should work everywhere, including |
michael@0 | 7 | * Big-Endian machines like the PowerPC. */ |
michael@0 | 8 | |
michael@0 | 9 | #include <stdlib.h> |
michael@0 | 10 | |
michael@0 | 11 | #include "mozilla/Endian.h" |
michael@0 | 12 | #include "nsICODecoder.h" |
michael@0 | 13 | |
michael@0 | 14 | #include "RasterImage.h" |
michael@0 | 15 | |
michael@0 | 16 | namespace mozilla { |
michael@0 | 17 | namespace image { |
michael@0 | 18 | |
michael@0 | 19 | #define ICONCOUNTOFFSET 4 |
michael@0 | 20 | #define DIRENTRYOFFSET 6 |
michael@0 | 21 | #define BITMAPINFOSIZE 40 |
michael@0 | 22 | #define PREFICONSIZE 16 |
michael@0 | 23 | |
michael@0 | 24 | // ---------------------------------------- |
michael@0 | 25 | // Actual Data Processing |
michael@0 | 26 | // ---------------------------------------- |
michael@0 | 27 | |
michael@0 | 28 | uint32_t |
michael@0 | 29 | nsICODecoder::CalcAlphaRowSize() |
michael@0 | 30 | { |
michael@0 | 31 | // Calculate rowsize in DWORD's and then return in # of bytes |
michael@0 | 32 | uint32_t rowSize = (GetRealWidth() + 31) / 32; // + 31 to round up |
michael@0 | 33 | return rowSize * 4; // Return rowSize in bytes |
michael@0 | 34 | } |
michael@0 | 35 | |
michael@0 | 36 | // Obtains the number of colors from the bits per pixel |
michael@0 | 37 | uint16_t |
michael@0 | 38 | nsICODecoder::GetNumColors() |
michael@0 | 39 | { |
michael@0 | 40 | uint16_t numColors = 0; |
michael@0 | 41 | if (mBPP <= 8) { |
michael@0 | 42 | switch (mBPP) { |
michael@0 | 43 | case 1: |
michael@0 | 44 | numColors = 2; |
michael@0 | 45 | break; |
michael@0 | 46 | case 4: |
michael@0 | 47 | numColors = 16; |
michael@0 | 48 | break; |
michael@0 | 49 | case 8: |
michael@0 | 50 | numColors = 256; |
michael@0 | 51 | break; |
michael@0 | 52 | default: |
michael@0 | 53 | numColors = (uint16_t)-1; |
michael@0 | 54 | } |
michael@0 | 55 | } |
michael@0 | 56 | return numColors; |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | |
michael@0 | 60 | nsICODecoder::nsICODecoder(RasterImage &aImage) |
michael@0 | 61 | : Decoder(aImage) |
michael@0 | 62 | { |
michael@0 | 63 | mPos = mImageOffset = mCurrIcon = mNumIcons = mBPP = mRowBytes = 0; |
michael@0 | 64 | mIsPNG = false; |
michael@0 | 65 | mRow = nullptr; |
michael@0 | 66 | mOldLine = mCurLine = 1; // Otherwise decoder will never start |
michael@0 | 67 | } |
michael@0 | 68 | |
michael@0 | 69 | nsICODecoder::~nsICODecoder() |
michael@0 | 70 | { |
michael@0 | 71 | if (mRow) { |
michael@0 | 72 | moz_free(mRow); |
michael@0 | 73 | } |
michael@0 | 74 | } |
michael@0 | 75 | |
michael@0 | 76 | void |
michael@0 | 77 | nsICODecoder::FinishInternal() |
michael@0 | 78 | { |
michael@0 | 79 | // We shouldn't be called in error cases |
michael@0 | 80 | NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call FinishInternal after error!"); |
michael@0 | 81 | |
michael@0 | 82 | // Finish the internally used decoder as well |
michael@0 | 83 | if (mContainedDecoder) { |
michael@0 | 84 | mContainedDecoder->FinishSharedDecoder(); |
michael@0 | 85 | mDecodeDone = mContainedDecoder->GetDecodeDone(); |
michael@0 | 86 | } |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | // Returns a buffer filled with the bitmap file header in little endian: |
michael@0 | 90 | // Signature 2 bytes 'BM' |
michael@0 | 91 | // FileSize 4 bytes File size in bytes |
michael@0 | 92 | // reserved 4 bytes unused (=0) |
michael@0 | 93 | // DataOffset 4 bytes File offset to Raster Data |
michael@0 | 94 | // Returns true if successful |
michael@0 | 95 | bool nsICODecoder::FillBitmapFileHeaderBuffer(int8_t *bfh) |
michael@0 | 96 | { |
michael@0 | 97 | memset(bfh, 0, 14); |
michael@0 | 98 | bfh[0] = 'B'; |
michael@0 | 99 | bfh[1] = 'M'; |
michael@0 | 100 | int32_t dataOffset = 0; |
michael@0 | 101 | int32_t fileSize = 0; |
michael@0 | 102 | dataOffset = BFH_LENGTH + BITMAPINFOSIZE; |
michael@0 | 103 | |
michael@0 | 104 | // The color table is present only if BPP is <= 8 |
michael@0 | 105 | if (mDirEntry.mBitCount <= 8) { |
michael@0 | 106 | uint16_t numColors = GetNumColors(); |
michael@0 | 107 | if (numColors == (uint16_t)-1) { |
michael@0 | 108 | return false; |
michael@0 | 109 | } |
michael@0 | 110 | dataOffset += 4 * numColors; |
michael@0 | 111 | fileSize = dataOffset + GetRealWidth() * GetRealHeight(); |
michael@0 | 112 | } else { |
michael@0 | 113 | fileSize = dataOffset + (mDirEntry.mBitCount * GetRealWidth() * |
michael@0 | 114 | GetRealHeight()) / 8; |
michael@0 | 115 | } |
michael@0 | 116 | |
michael@0 | 117 | NativeEndian::swapToLittleEndianInPlace(&fileSize, 1); |
michael@0 | 118 | memcpy(bfh + 2, &fileSize, sizeof(fileSize)); |
michael@0 | 119 | NativeEndian::swapToLittleEndianInPlace(&dataOffset, 1); |
michael@0 | 120 | memcpy(bfh + 10, &dataOffset, sizeof(dataOffset)); |
michael@0 | 121 | return true; |
michael@0 | 122 | } |
michael@0 | 123 | |
michael@0 | 124 | // A BMP inside of an ICO has *2 height because of the AND mask |
michael@0 | 125 | // that follows the actual bitmap. The BMP shouldn't know about |
michael@0 | 126 | // this difference though. |
michael@0 | 127 | bool |
michael@0 | 128 | nsICODecoder::FixBitmapHeight(int8_t *bih) |
michael@0 | 129 | { |
michael@0 | 130 | // Get the height from the BMP file information header |
michael@0 | 131 | int32_t height; |
michael@0 | 132 | memcpy(&height, bih + 8, sizeof(height)); |
michael@0 | 133 | NativeEndian::swapFromLittleEndianInPlace(&height, 1); |
michael@0 | 134 | // BMPs can be stored inverted by having a negative height |
michael@0 | 135 | height = abs(height); |
michael@0 | 136 | |
michael@0 | 137 | // The bitmap height is by definition * 2 what it should be to account for |
michael@0 | 138 | // the 'AND mask'. It is * 2 even if the `AND mask` is not present. |
michael@0 | 139 | height /= 2; |
michael@0 | 140 | |
michael@0 | 141 | if (height > 256) { |
michael@0 | 142 | return false; |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | // We should always trust the height from the bitmap itself instead of |
michael@0 | 146 | // the ICO height. So fix the ICO height. |
michael@0 | 147 | if (height == 256) { |
michael@0 | 148 | mDirEntry.mHeight = 0; |
michael@0 | 149 | } else { |
michael@0 | 150 | mDirEntry.mHeight = (int8_t)height; |
michael@0 | 151 | } |
michael@0 | 152 | |
michael@0 | 153 | // Fix the BMP height in the BIH so that the BMP decoder can work properly |
michael@0 | 154 | NativeEndian::swapToLittleEndianInPlace(&height, 1); |
michael@0 | 155 | memcpy(bih + 8, &height, sizeof(height)); |
michael@0 | 156 | return true; |
michael@0 | 157 | } |
michael@0 | 158 | |
michael@0 | 159 | // We should always trust the contained resource for the width |
michael@0 | 160 | // information over our own information. |
michael@0 | 161 | bool |
michael@0 | 162 | nsICODecoder::FixBitmapWidth(int8_t *bih) |
michael@0 | 163 | { |
michael@0 | 164 | // Get the width from the BMP file information header |
michael@0 | 165 | int32_t width; |
michael@0 | 166 | memcpy(&width, bih + 4, sizeof(width)); |
michael@0 | 167 | NativeEndian::swapFromLittleEndianInPlace(&width, 1); |
michael@0 | 168 | if (width > 256) { |
michael@0 | 169 | return false; |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | // We should always trust the width from the bitmap itself instead of |
michael@0 | 173 | // the ICO width. |
michael@0 | 174 | if (width == 256) { |
michael@0 | 175 | mDirEntry.mWidth = 0; |
michael@0 | 176 | } else { |
michael@0 | 177 | mDirEntry.mWidth = (int8_t)width; |
michael@0 | 178 | } |
michael@0 | 179 | return true; |
michael@0 | 180 | } |
michael@0 | 181 | |
michael@0 | 182 | // The BMP information header's bits per pixel should be trusted |
michael@0 | 183 | // more than what we have. Usually the ICO's BPP is set to 0 |
michael@0 | 184 | int32_t |
michael@0 | 185 | nsICODecoder::ExtractBPPFromBitmap(int8_t *bih) |
michael@0 | 186 | { |
michael@0 | 187 | int32_t bitsPerPixel; |
michael@0 | 188 | memcpy(&bitsPerPixel, bih + 14, sizeof(bitsPerPixel)); |
michael@0 | 189 | NativeEndian::swapFromLittleEndianInPlace(&bitsPerPixel, 1); |
michael@0 | 190 | return bitsPerPixel; |
michael@0 | 191 | } |
michael@0 | 192 | |
michael@0 | 193 | int32_t |
michael@0 | 194 | nsICODecoder::ExtractBIHSizeFromBitmap(int8_t *bih) |
michael@0 | 195 | { |
michael@0 | 196 | int32_t headerSize; |
michael@0 | 197 | memcpy(&headerSize, bih, sizeof(headerSize)); |
michael@0 | 198 | NativeEndian::swapFromLittleEndianInPlace(&headerSize, 1); |
michael@0 | 199 | return headerSize; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | void |
michael@0 | 203 | nsICODecoder::SetHotSpotIfCursor() { |
michael@0 | 204 | if (!mIsCursor) { |
michael@0 | 205 | return; |
michael@0 | 206 | } |
michael@0 | 207 | |
michael@0 | 208 | mImageMetadata.SetHotspot(mDirEntry.mXHotspot, mDirEntry.mYHotspot); |
michael@0 | 209 | } |
michael@0 | 210 | |
michael@0 | 211 | void |
michael@0 | 212 | nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy) |
michael@0 | 213 | { |
michael@0 | 214 | NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!"); |
michael@0 | 215 | |
michael@0 | 216 | if (!aCount) { |
michael@0 | 217 | if (mContainedDecoder) { |
michael@0 | 218 | WriteToContainedDecoder(aBuffer, aCount, aStrategy); |
michael@0 | 219 | } |
michael@0 | 220 | return; |
michael@0 | 221 | } |
michael@0 | 222 | |
michael@0 | 223 | while (aCount && (mPos < ICONCOUNTOFFSET)) { // Skip to the # of icons. |
michael@0 | 224 | if (mPos == 2) { // if the third byte is 1: This is an icon, 2: a cursor |
michael@0 | 225 | if ((*aBuffer != 1) && (*aBuffer != 2)) { |
michael@0 | 226 | PostDataError(); |
michael@0 | 227 | return; |
michael@0 | 228 | } |
michael@0 | 229 | mIsCursor = (*aBuffer == 2); |
michael@0 | 230 | } |
michael@0 | 231 | mPos++; aBuffer++; aCount--; |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | if (mPos == ICONCOUNTOFFSET && aCount >= 2) { |
michael@0 | 235 | mNumIcons = LittleEndian::readUint16(reinterpret_cast<const uint16_t*>(aBuffer)); |
michael@0 | 236 | aBuffer += 2; |
michael@0 | 237 | mPos += 2; |
michael@0 | 238 | aCount -= 2; |
michael@0 | 239 | } |
michael@0 | 240 | |
michael@0 | 241 | if (mNumIcons == 0) |
michael@0 | 242 | return; // Nothing to do. |
michael@0 | 243 | |
michael@0 | 244 | uint16_t colorDepth = 0; |
michael@0 | 245 | nsIntSize prefSize = mImage.GetRequestedResolution(); |
michael@0 | 246 | if (prefSize.width == 0 && prefSize.height == 0) { |
michael@0 | 247 | prefSize.SizeTo(PREFICONSIZE, PREFICONSIZE); |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | // A measure of the difference in size between the entry we've found |
michael@0 | 251 | // and the requested size. We will choose the smallest image that is |
michael@0 | 252 | // >= requested size (i.e. we assume it's better to downscale a larger |
michael@0 | 253 | // icon than to upscale a smaller one). |
michael@0 | 254 | int32_t diff = INT_MIN; |
michael@0 | 255 | |
michael@0 | 256 | // Loop through each entry's dir entry |
michael@0 | 257 | while (mCurrIcon < mNumIcons) { |
michael@0 | 258 | if (mPos >= DIRENTRYOFFSET + (mCurrIcon * sizeof(mDirEntryArray)) && |
michael@0 | 259 | mPos < DIRENTRYOFFSET + ((mCurrIcon + 1) * sizeof(mDirEntryArray))) { |
michael@0 | 260 | uint32_t toCopy = sizeof(mDirEntryArray) - |
michael@0 | 261 | (mPos - DIRENTRYOFFSET - mCurrIcon * sizeof(mDirEntryArray)); |
michael@0 | 262 | if (toCopy > aCount) { |
michael@0 | 263 | toCopy = aCount; |
michael@0 | 264 | } |
michael@0 | 265 | memcpy(mDirEntryArray + sizeof(mDirEntryArray) - toCopy, aBuffer, toCopy); |
michael@0 | 266 | mPos += toCopy; |
michael@0 | 267 | aCount -= toCopy; |
michael@0 | 268 | aBuffer += toCopy; |
michael@0 | 269 | } |
michael@0 | 270 | if (aCount == 0) |
michael@0 | 271 | return; // Need more data |
michael@0 | 272 | |
michael@0 | 273 | IconDirEntry e; |
michael@0 | 274 | if (mPos == (DIRENTRYOFFSET + ICODIRENTRYSIZE) + |
michael@0 | 275 | (mCurrIcon * sizeof(mDirEntryArray))) { |
michael@0 | 276 | mCurrIcon++; |
michael@0 | 277 | ProcessDirEntry(e); |
michael@0 | 278 | // We can't use GetRealWidth and GetRealHeight here because those operate |
michael@0 | 279 | // on mDirEntry, here we are going through each item in the directory. |
michael@0 | 280 | // Calculate the delta between this image's size and the desired size, |
michael@0 | 281 | // so we can see if it is better than our current-best option. |
michael@0 | 282 | // In the case of several equally-good images, we use the last one. |
michael@0 | 283 | int32_t delta = (e.mWidth == 0 ? 256 : e.mWidth) - prefSize.width + |
michael@0 | 284 | (e.mHeight == 0 ? 256 : e.mHeight) - prefSize.height; |
michael@0 | 285 | if (e.mBitCount >= colorDepth && |
michael@0 | 286 | ((diff < 0 && delta >= diff) || (delta >= 0 && delta <= diff))) { |
michael@0 | 287 | diff = delta; |
michael@0 | 288 | mImageOffset = e.mImageOffset; |
michael@0 | 289 | |
michael@0 | 290 | // ensure mImageOffset is >= size of the direntry headers (bug #245631) |
michael@0 | 291 | uint32_t minImageOffset = DIRENTRYOFFSET + |
michael@0 | 292 | mNumIcons * sizeof(mDirEntryArray); |
michael@0 | 293 | if (mImageOffset < minImageOffset) { |
michael@0 | 294 | PostDataError(); |
michael@0 | 295 | return; |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | colorDepth = e.mBitCount; |
michael@0 | 299 | memcpy(&mDirEntry, &e, sizeof(IconDirEntry)); |
michael@0 | 300 | } |
michael@0 | 301 | } |
michael@0 | 302 | } |
michael@0 | 303 | |
michael@0 | 304 | if (mPos < mImageOffset) { |
michael@0 | 305 | // Skip to (or at least towards) the desired image offset |
michael@0 | 306 | uint32_t toSkip = mImageOffset - mPos; |
michael@0 | 307 | if (toSkip > aCount) |
michael@0 | 308 | toSkip = aCount; |
michael@0 | 309 | |
michael@0 | 310 | mPos += toSkip; |
michael@0 | 311 | aBuffer += toSkip; |
michael@0 | 312 | aCount -= toSkip; |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | // If we are within the first PNGSIGNATURESIZE bytes of the image data, |
michael@0 | 316 | // then we have either a BMP or a PNG. We use the first PNGSIGNATURESIZE |
michael@0 | 317 | // bytes to determine which one we have. |
michael@0 | 318 | if (mCurrIcon == mNumIcons && mPos >= mImageOffset && |
michael@0 | 319 | mPos < mImageOffset + PNGSIGNATURESIZE) |
michael@0 | 320 | { |
michael@0 | 321 | uint32_t toCopy = PNGSIGNATURESIZE - (mPos - mImageOffset); |
michael@0 | 322 | if (toCopy > aCount) { |
michael@0 | 323 | toCopy = aCount; |
michael@0 | 324 | } |
michael@0 | 325 | |
michael@0 | 326 | memcpy(mSignature + (mPos - mImageOffset), aBuffer, toCopy); |
michael@0 | 327 | mPos += toCopy; |
michael@0 | 328 | aCount -= toCopy; |
michael@0 | 329 | aBuffer += toCopy; |
michael@0 | 330 | |
michael@0 | 331 | mIsPNG = !memcmp(mSignature, nsPNGDecoder::pngSignatureBytes, |
michael@0 | 332 | PNGSIGNATURESIZE); |
michael@0 | 333 | if (mIsPNG) { |
michael@0 | 334 | mContainedDecoder = new nsPNGDecoder(mImage); |
michael@0 | 335 | mContainedDecoder->SetObserver(mObserver); |
michael@0 | 336 | mContainedDecoder->SetSizeDecode(IsSizeDecode()); |
michael@0 | 337 | mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength, |
michael@0 | 338 | mColormap, mColormapSize, |
michael@0 | 339 | mCurrentFrame); |
michael@0 | 340 | if (!WriteToContainedDecoder(mSignature, PNGSIGNATURESIZE, aStrategy)) { |
michael@0 | 341 | return; |
michael@0 | 342 | } |
michael@0 | 343 | } |
michael@0 | 344 | } |
michael@0 | 345 | |
michael@0 | 346 | // If we have a PNG, let the PNG decoder do all of the rest of the work |
michael@0 | 347 | if (mIsPNG && mContainedDecoder && mPos >= mImageOffset + PNGSIGNATURESIZE) { |
michael@0 | 348 | if (!WriteToContainedDecoder(aBuffer, aCount, aStrategy)) { |
michael@0 | 349 | return; |
michael@0 | 350 | } |
michael@0 | 351 | |
michael@0 | 352 | if (!HasSize() && mContainedDecoder->HasSize()) { |
michael@0 | 353 | PostSize(mContainedDecoder->GetImageMetadata().GetWidth(), |
michael@0 | 354 | mContainedDecoder->GetImageMetadata().GetHeight()); |
michael@0 | 355 | } |
michael@0 | 356 | |
michael@0 | 357 | mPos += aCount; |
michael@0 | 358 | aBuffer += aCount; |
michael@0 | 359 | aCount = 0; |
michael@0 | 360 | |
michael@0 | 361 | // Raymond Chen says that 32bpp only are valid PNG ICOs |
michael@0 | 362 | // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx |
michael@0 | 363 | if (!IsSizeDecode() && |
michael@0 | 364 | !static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) { |
michael@0 | 365 | PostDataError(); |
michael@0 | 366 | } |
michael@0 | 367 | return; |
michael@0 | 368 | } |
michael@0 | 369 | |
michael@0 | 370 | // We've processed all of the icon dir entries and are within the |
michael@0 | 371 | // bitmap info size |
michael@0 | 372 | if (!mIsPNG && mCurrIcon == mNumIcons && mPos >= mImageOffset && |
michael@0 | 373 | mPos >= mImageOffset + PNGSIGNATURESIZE && |
michael@0 | 374 | mPos < mImageOffset + BITMAPINFOSIZE) { |
michael@0 | 375 | |
michael@0 | 376 | // As we were decoding, we did not know if we had a PNG signature or the |
michael@0 | 377 | // start of a bitmap information header. At this point we know we had |
michael@0 | 378 | // a bitmap information header and not a PNG signature, so fill the bitmap |
michael@0 | 379 | // information header with the data it should already have. |
michael@0 | 380 | memcpy(mBIHraw, mSignature, PNGSIGNATURESIZE); |
michael@0 | 381 | |
michael@0 | 382 | // We've found the icon. |
michael@0 | 383 | uint32_t toCopy = sizeof(mBIHraw) - (mPos - mImageOffset); |
michael@0 | 384 | if (toCopy > aCount) |
michael@0 | 385 | toCopy = aCount; |
michael@0 | 386 | |
michael@0 | 387 | memcpy(mBIHraw + (mPos - mImageOffset), aBuffer, toCopy); |
michael@0 | 388 | mPos += toCopy; |
michael@0 | 389 | aCount -= toCopy; |
michael@0 | 390 | aBuffer += toCopy; |
michael@0 | 391 | } |
michael@0 | 392 | |
michael@0 | 393 | // If we have a BMP inside the ICO and we have read the BIH header |
michael@0 | 394 | if (!mIsPNG && mPos == mImageOffset + BITMAPINFOSIZE) { |
michael@0 | 395 | |
michael@0 | 396 | // Make sure we have a sane value for the bitmap information header |
michael@0 | 397 | int32_t bihSize = ExtractBIHSizeFromBitmap(reinterpret_cast<int8_t*>(mBIHraw)); |
michael@0 | 398 | if (bihSize != BITMAPINFOSIZE) { |
michael@0 | 399 | PostDataError(); |
michael@0 | 400 | return; |
michael@0 | 401 | } |
michael@0 | 402 | // We are extracting the BPP from the BIH header as it should be trusted |
michael@0 | 403 | // over the one we have from the icon header |
michael@0 | 404 | mBPP = ExtractBPPFromBitmap(reinterpret_cast<int8_t*>(mBIHraw)); |
michael@0 | 405 | |
michael@0 | 406 | // Init the bitmap decoder which will do most of the work for us |
michael@0 | 407 | // It will do everything except the AND mask which isn't present in bitmaps |
michael@0 | 408 | // bmpDecoder is for local scope ease, it will be freed by mContainedDecoder |
michael@0 | 409 | nsBMPDecoder *bmpDecoder = new nsBMPDecoder(mImage); |
michael@0 | 410 | mContainedDecoder = bmpDecoder; |
michael@0 | 411 | bmpDecoder->SetUseAlphaData(true); |
michael@0 | 412 | mContainedDecoder->SetObserver(mObserver); |
michael@0 | 413 | mContainedDecoder->SetSizeDecode(IsSizeDecode()); |
michael@0 | 414 | mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength, |
michael@0 | 415 | mColormap, mColormapSize, |
michael@0 | 416 | mCurrentFrame); |
michael@0 | 417 | |
michael@0 | 418 | // The ICO format when containing a BMP does not include the 14 byte |
michael@0 | 419 | // bitmap file header. To use the code of the BMP decoder we need to |
michael@0 | 420 | // generate this header ourselves and feed it to the BMP decoder. |
michael@0 | 421 | int8_t bfhBuffer[BMPFILEHEADERSIZE]; |
michael@0 | 422 | if (!FillBitmapFileHeaderBuffer(bfhBuffer)) { |
michael@0 | 423 | PostDataError(); |
michael@0 | 424 | return; |
michael@0 | 425 | } |
michael@0 | 426 | if (!WriteToContainedDecoder((const char*)bfhBuffer, sizeof(bfhBuffer), aStrategy)) { |
michael@0 | 427 | return; |
michael@0 | 428 | } |
michael@0 | 429 | |
michael@0 | 430 | // Setup the cursor hot spot if one is present |
michael@0 | 431 | SetHotSpotIfCursor(); |
michael@0 | 432 | |
michael@0 | 433 | // Fix the ICO height from the BIH. |
michael@0 | 434 | // Fix the height on the BIH to be /2 so our BMP decoder will understand. |
michael@0 | 435 | if (!FixBitmapHeight(reinterpret_cast<int8_t*>(mBIHraw))) { |
michael@0 | 436 | PostDataError(); |
michael@0 | 437 | return; |
michael@0 | 438 | } |
michael@0 | 439 | |
michael@0 | 440 | // Fix the ICO width from the BIH. |
michael@0 | 441 | if (!FixBitmapWidth(reinterpret_cast<int8_t*>(mBIHraw))) { |
michael@0 | 442 | PostDataError(); |
michael@0 | 443 | return; |
michael@0 | 444 | } |
michael@0 | 445 | |
michael@0 | 446 | // Write out the BMP's bitmap info header |
michael@0 | 447 | if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw), aStrategy)) { |
michael@0 | 448 | return; |
michael@0 | 449 | } |
michael@0 | 450 | |
michael@0 | 451 | PostSize(mContainedDecoder->GetImageMetadata().GetWidth(), |
michael@0 | 452 | mContainedDecoder->GetImageMetadata().GetHeight()); |
michael@0 | 453 | |
michael@0 | 454 | // We have the size. If we're doing a size decode, we got what |
michael@0 | 455 | // we came for. |
michael@0 | 456 | if (IsSizeDecode()) |
michael@0 | 457 | return; |
michael@0 | 458 | |
michael@0 | 459 | // Sometimes the ICO BPP header field is not filled out |
michael@0 | 460 | // so we should trust the contained resource over our own |
michael@0 | 461 | // information. |
michael@0 | 462 | mBPP = bmpDecoder->GetBitsPerPixel(); |
michael@0 | 463 | |
michael@0 | 464 | // Check to make sure we have valid color settings |
michael@0 | 465 | uint16_t numColors = GetNumColors(); |
michael@0 | 466 | if (numColors == (uint16_t)-1) { |
michael@0 | 467 | PostDataError(); |
michael@0 | 468 | return; |
michael@0 | 469 | } |
michael@0 | 470 | } |
michael@0 | 471 | |
michael@0 | 472 | // If we have a BMP |
michael@0 | 473 | if (!mIsPNG && mContainedDecoder && mPos >= mImageOffset + BITMAPINFOSIZE) { |
michael@0 | 474 | uint16_t numColors = GetNumColors(); |
michael@0 | 475 | if (numColors == (uint16_t)-1) { |
michael@0 | 476 | PostDataError(); |
michael@0 | 477 | return; |
michael@0 | 478 | } |
michael@0 | 479 | // Feed the actual image data (not including headers) into the BMP decoder |
michael@0 | 480 | uint32_t bmpDataOffset = mDirEntry.mImageOffset + BITMAPINFOSIZE; |
michael@0 | 481 | uint32_t bmpDataEnd = mDirEntry.mImageOffset + BITMAPINFOSIZE + |
michael@0 | 482 | static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetCompressedImageSize() + |
michael@0 | 483 | 4 * numColors; |
michael@0 | 484 | |
michael@0 | 485 | // If we are feeding in the core image data, but we have not yet |
michael@0 | 486 | // reached the ICO's 'AND buffer mask' |
michael@0 | 487 | if (mPos >= bmpDataOffset && mPos < bmpDataEnd) { |
michael@0 | 488 | |
michael@0 | 489 | // Figure out how much data the BMP decoder wants |
michael@0 | 490 | uint32_t toFeed = bmpDataEnd - mPos; |
michael@0 | 491 | if (toFeed > aCount) { |
michael@0 | 492 | toFeed = aCount; |
michael@0 | 493 | } |
michael@0 | 494 | |
michael@0 | 495 | if (!WriteToContainedDecoder(aBuffer, toFeed, aStrategy)) { |
michael@0 | 496 | return; |
michael@0 | 497 | } |
michael@0 | 498 | |
michael@0 | 499 | mPos += toFeed; |
michael@0 | 500 | aCount -= toFeed; |
michael@0 | 501 | aBuffer += toFeed; |
michael@0 | 502 | } |
michael@0 | 503 | |
michael@0 | 504 | // If the bitmap is fully processed, treat any left over data as the ICO's |
michael@0 | 505 | // 'AND buffer mask' which appears after the bitmap resource. |
michael@0 | 506 | if (!mIsPNG && mPos >= bmpDataEnd) { |
michael@0 | 507 | // There may be an optional AND bit mask after the data. This is |
michael@0 | 508 | // only used if the alpha data is not already set. The alpha data |
michael@0 | 509 | // is used for 32bpp bitmaps as per the comment in ICODecoder.h |
michael@0 | 510 | // The alpha mask should be checked in all other cases. |
michael@0 | 511 | if (static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetBitsPerPixel() != 32 || |
michael@0 | 512 | !static_cast<nsBMPDecoder*>(mContainedDecoder.get())->HasAlphaData()) { |
michael@0 | 513 | uint32_t rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up |
michael@0 | 514 | if (mPos == bmpDataEnd) { |
michael@0 | 515 | mPos++; |
michael@0 | 516 | mRowBytes = 0; |
michael@0 | 517 | mCurLine = GetRealHeight(); |
michael@0 | 518 | mRow = (uint8_t*)moz_realloc(mRow, rowSize); |
michael@0 | 519 | if (!mRow) { |
michael@0 | 520 | PostDecoderError(NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 521 | return; |
michael@0 | 522 | } |
michael@0 | 523 | } |
michael@0 | 524 | |
michael@0 | 525 | // Ensure memory has been allocated before decoding. |
michael@0 | 526 | NS_ABORT_IF_FALSE(mRow, "mRow is null"); |
michael@0 | 527 | if (!mRow) { |
michael@0 | 528 | PostDataError(); |
michael@0 | 529 | return; |
michael@0 | 530 | } |
michael@0 | 531 | |
michael@0 | 532 | while (mCurLine > 0 && aCount > 0) { |
michael@0 | 533 | uint32_t toCopy = std::min(rowSize - mRowBytes, aCount); |
michael@0 | 534 | if (toCopy) { |
michael@0 | 535 | memcpy(mRow + mRowBytes, aBuffer, toCopy); |
michael@0 | 536 | aCount -= toCopy; |
michael@0 | 537 | aBuffer += toCopy; |
michael@0 | 538 | mRowBytes += toCopy; |
michael@0 | 539 | } |
michael@0 | 540 | if (rowSize == mRowBytes) { |
michael@0 | 541 | mCurLine--; |
michael@0 | 542 | mRowBytes = 0; |
michael@0 | 543 | |
michael@0 | 544 | uint32_t* imageData = |
michael@0 | 545 | static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetImageData(); |
michael@0 | 546 | if (!imageData) { |
michael@0 | 547 | PostDataError(); |
michael@0 | 548 | return; |
michael@0 | 549 | } |
michael@0 | 550 | uint32_t* decoded = imageData + mCurLine * GetRealWidth(); |
michael@0 | 551 | uint32_t* decoded_end = decoded + GetRealWidth(); |
michael@0 | 552 | uint8_t* p = mRow, *p_end = mRow + rowSize; |
michael@0 | 553 | while (p < p_end) { |
michael@0 | 554 | uint8_t idx = *p++; |
michael@0 | 555 | for (uint8_t bit = 0x80; bit && decoded<decoded_end; bit >>= 1) { |
michael@0 | 556 | // Clear pixel completely for transparency. |
michael@0 | 557 | if (idx & bit) { |
michael@0 | 558 | *decoded = 0; |
michael@0 | 559 | } |
michael@0 | 560 | decoded++; |
michael@0 | 561 | } |
michael@0 | 562 | } |
michael@0 | 563 | } |
michael@0 | 564 | } |
michael@0 | 565 | } |
michael@0 | 566 | } |
michael@0 | 567 | } |
michael@0 | 568 | } |
michael@0 | 569 | |
michael@0 | 570 | bool |
michael@0 | 571 | nsICODecoder::WriteToContainedDecoder(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy) |
michael@0 | 572 | { |
michael@0 | 573 | mContainedDecoder->Write(aBuffer, aCount, aStrategy); |
michael@0 | 574 | if (mContainedDecoder->HasDataError()) { |
michael@0 | 575 | mDataError = mContainedDecoder->HasDataError(); |
michael@0 | 576 | } |
michael@0 | 577 | if (mContainedDecoder->HasDecoderError()) { |
michael@0 | 578 | PostDecoderError(mContainedDecoder->GetDecoderError()); |
michael@0 | 579 | } |
michael@0 | 580 | return !HasError(); |
michael@0 | 581 | } |
michael@0 | 582 | |
michael@0 | 583 | void |
michael@0 | 584 | nsICODecoder::ProcessDirEntry(IconDirEntry& aTarget) |
michael@0 | 585 | { |
michael@0 | 586 | memset(&aTarget, 0, sizeof(aTarget)); |
michael@0 | 587 | memcpy(&aTarget.mWidth, mDirEntryArray, sizeof(aTarget.mWidth)); |
michael@0 | 588 | memcpy(&aTarget.mHeight, mDirEntryArray + 1, sizeof(aTarget.mHeight)); |
michael@0 | 589 | memcpy(&aTarget.mColorCount, mDirEntryArray + 2, sizeof(aTarget.mColorCount)); |
michael@0 | 590 | memcpy(&aTarget.mReserved, mDirEntryArray + 3, sizeof(aTarget.mReserved)); |
michael@0 | 591 | memcpy(&aTarget.mPlanes, mDirEntryArray + 4, sizeof(aTarget.mPlanes)); |
michael@0 | 592 | aTarget.mPlanes = LittleEndian::readUint16(&aTarget.mPlanes); |
michael@0 | 593 | memcpy(&aTarget.mBitCount, mDirEntryArray + 6, sizeof(aTarget.mBitCount)); |
michael@0 | 594 | aTarget.mBitCount = LittleEndian::readUint16(&aTarget.mBitCount); |
michael@0 | 595 | memcpy(&aTarget.mBytesInRes, mDirEntryArray + 8, sizeof(aTarget.mBytesInRes)); |
michael@0 | 596 | aTarget.mBytesInRes = LittleEndian::readUint32(&aTarget.mBytesInRes); |
michael@0 | 597 | memcpy(&aTarget.mImageOffset, mDirEntryArray + 12, |
michael@0 | 598 | sizeof(aTarget.mImageOffset)); |
michael@0 | 599 | aTarget.mImageOffset = LittleEndian::readUint32(&aTarget.mImageOffset); |
michael@0 | 600 | } |
michael@0 | 601 | |
michael@0 | 602 | bool |
michael@0 | 603 | nsICODecoder::NeedsNewFrame() const |
michael@0 | 604 | { |
michael@0 | 605 | if (mContainedDecoder) { |
michael@0 | 606 | return mContainedDecoder->NeedsNewFrame(); |
michael@0 | 607 | } |
michael@0 | 608 | |
michael@0 | 609 | return Decoder::NeedsNewFrame(); |
michael@0 | 610 | } |
michael@0 | 611 | |
michael@0 | 612 | nsresult |
michael@0 | 613 | nsICODecoder::AllocateFrame() |
michael@0 | 614 | { |
michael@0 | 615 | if (mContainedDecoder) { |
michael@0 | 616 | nsresult rv = mContainedDecoder->AllocateFrame(); |
michael@0 | 617 | mCurrentFrame = mContainedDecoder->GetCurrentFrame(); |
michael@0 | 618 | return rv; |
michael@0 | 619 | } |
michael@0 | 620 | |
michael@0 | 621 | return Decoder::AllocateFrame(); |
michael@0 | 622 | } |
michael@0 | 623 | |
michael@0 | 624 | } // namespace image |
michael@0 | 625 | } // namespace mozilla |