nsICODecoder.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. /* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /* This is a Cross-Platform ICO Decoder, which should work everywhere, including
  6. * Big-Endian machines like the PowerPC. */
  7. #include "nsICODecoder.h"
  8. #include <stdlib.h>
  9. #include "mozilla/EndianUtils.h"
  10. #include "mozilla/Move.h"
  11. #include "RasterImage.h"
  12. using namespace mozilla::gfx;
  13. namespace mozilla {
  14. namespace image {
  15. // Constants.
  16. static const uint32_t ICOHEADERSIZE = 6;
  17. static const uint32_t BITMAPINFOSIZE = bmp::InfoHeaderLength::WIN_ICO;
  18. // ----------------------------------------
  19. // Actual Data Processing
  20. // ----------------------------------------
  21. // Obtains the number of colors from the bits per pixel
  22. uint16_t
  23. nsICODecoder::GetNumColors()
  24. {
  25. uint16_t numColors = 0;
  26. if (mBPP <= 8) {
  27. switch (mBPP) {
  28. case 1:
  29. numColors = 2;
  30. break;
  31. case 4:
  32. numColors = 16;
  33. break;
  34. case 8:
  35. numColors = 256;
  36. break;
  37. default:
  38. numColors = (uint16_t)-1;
  39. }
  40. }
  41. return numColors;
  42. }
  43. nsICODecoder::nsICODecoder(RasterImage* aImage)
  44. : Decoder(aImage)
  45. , mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE),
  46. Transition::TerminateSuccess())
  47. , mBiggestResourceColorDepth(0)
  48. , mBestResourceDelta(INT_MIN)
  49. , mBestResourceColorDepth(0)
  50. , mNumIcons(0)
  51. , mCurrIcon(0)
  52. , mBPP(0)
  53. , mMaskRowSize(0)
  54. , mCurrMaskLine(0)
  55. , mIsCursor(false)
  56. , mHasMaskAlpha(false)
  57. { }
  58. nsresult
  59. nsICODecoder::FinishInternal()
  60. {
  61. // We shouldn't be called in error cases
  62. MOZ_ASSERT(!HasError(), "Shouldn't call FinishInternal after error!");
  63. return GetFinalStateFromContainedDecoder();
  64. }
  65. nsresult
  66. nsICODecoder::FinishWithErrorInternal()
  67. {
  68. return GetFinalStateFromContainedDecoder();
  69. }
  70. nsresult
  71. nsICODecoder::GetFinalStateFromContainedDecoder()
  72. {
  73. if (!mContainedDecoder) {
  74. return NS_OK;
  75. }
  76. MOZ_ASSERT(mContainedSourceBuffer,
  77. "Should have a SourceBuffer if we have a decoder");
  78. // Let the contained decoder finish up if necessary.
  79. if (!mContainedSourceBuffer->IsComplete()) {
  80. mContainedSourceBuffer->Complete(NS_OK);
  81. mContainedDecoder->Decode();
  82. }
  83. // Make our state the same as the state of the contained decoder.
  84. mDecodeDone = mContainedDecoder->GetDecodeDone();
  85. mProgress |= mContainedDecoder->TakeProgress();
  86. mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
  87. mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
  88. // Propagate errors.
  89. nsresult rv = HasError() || mContainedDecoder->HasError()
  90. ? NS_ERROR_FAILURE
  91. : NS_OK;
  92. MOZ_ASSERT(NS_FAILED(rv) || !mCurrentFrame || mCurrentFrame->IsFinished());
  93. return rv;
  94. }
  95. bool
  96. nsICODecoder::CheckAndFixBitmapSize(int8_t* aBIH)
  97. {
  98. // Get the width from the BMP file information header. This is
  99. // (unintuitively) a signed integer; see the documentation at:
  100. //
  101. // https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
  102. //
  103. // However, we reject negative widths since they aren't meaningful.
  104. const int32_t width = LittleEndian::readInt32(aBIH + 4);
  105. if (width <= 0 || width > 256) {
  106. return false;
  107. }
  108. // Verify that the BMP width matches the width we got from the ICO directory
  109. // entry. If not, decoding fails, because if we were to allow it to continue
  110. // the intrinsic size of the image wouldn't match the size of the decoded
  111. // surface.
  112. if (width != int32_t(GetRealWidth())) {
  113. return false;
  114. }
  115. // Get the height from the BMP file information header. This is also signed,
  116. // but in this case negative values are meaningful; see below.
  117. int32_t height = LittleEndian::readInt32(aBIH + 8);
  118. if (height == 0) {
  119. return false;
  120. }
  121. // BMPs can be stored inverted by having a negative height.
  122. // XXX(seth): Should we really be writing the absolute value into the BIH
  123. // below? Seems like this could be problematic for inverted BMPs.
  124. height = abs(height);
  125. // The height field is double the actual height of the image to account for
  126. // the AND mask. This is true even if the AND mask is not present.
  127. height /= 2;
  128. if (height > 256) {
  129. return false;
  130. }
  131. // Verify that the BMP height matches the height we got from the ICO directory
  132. // entry. If not, again, decoding fails.
  133. if (height != int32_t(GetRealHeight())) {
  134. return false;
  135. }
  136. // Fix the BMP height in the BIH so that the BMP decoder, which does not know
  137. // about the AND mask that may follow the actual bitmap, can work properly.
  138. LittleEndian::writeInt32(aBIH + 8, GetRealHeight());
  139. return true;
  140. }
  141. LexerTransition<ICOState>
  142. nsICODecoder::ReadHeader(const char* aData)
  143. {
  144. // If the third byte is 1, this is an icon. If 2, a cursor.
  145. if ((aData[2] != 1) && (aData[2] != 2)) {
  146. return Transition::TerminateFailure();
  147. }
  148. mIsCursor = (aData[2] == 2);
  149. // The fifth and sixth bytes specify the number of resources in the file.
  150. mNumIcons = LittleEndian::readUint16(aData + 4);
  151. if (mNumIcons == 0) {
  152. return Transition::TerminateSuccess(); // Nothing to do.
  153. }
  154. // Downscale-during-decode can end up decoding different resources in the ICO
  155. // file depending on the target size. Since the resources are not necessarily
  156. // scaled versions of the same image, some may be transparent and some may not
  157. // be. We could be precise about transparency if we decoded the metadata of
  158. // every resource, but for now we don't and it's safest to assume that
  159. // transparency could be present.
  160. PostHasTransparency();
  161. return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
  162. }
  163. size_t
  164. nsICODecoder::FirstResourceOffset() const
  165. {
  166. MOZ_ASSERT(mNumIcons > 0,
  167. "Calling FirstResourceOffset before processing header");
  168. // The first resource starts right after the directory, which starts right
  169. // after the ICO header.
  170. return ICOHEADERSIZE + mNumIcons * ICODIRENTRYSIZE;
  171. }
  172. LexerTransition<ICOState>
  173. nsICODecoder::ReadDirEntry(const char* aData)
  174. {
  175. mCurrIcon++;
  176. // Read the directory entry.
  177. IconDirEntry e;
  178. e.mWidth = aData[0];
  179. e.mHeight = aData[1];
  180. e.mColorCount = aData[2];
  181. e.mReserved = aData[3];
  182. e.mPlanes = LittleEndian::readUint16(aData + 4);
  183. e.mBitCount = LittleEndian::readUint16(aData + 6);
  184. e.mBytesInRes = LittleEndian::readUint32(aData + 8);
  185. e.mImageOffset = LittleEndian::readUint32(aData + 12);
  186. // If an explicit output size was specified, we'll try to select the resource
  187. // that matches it best below.
  188. const Maybe<IntSize> desiredSize = ExplicitOutputSize();
  189. // Determine if this is the biggest resource we've seen so far. We always use
  190. // the biggest resource for the intrinsic size, and if we don't have a
  191. // specific desired size, we select it as the best resource as well.
  192. IntSize entrySize(GetRealWidth(e), GetRealHeight(e));
  193. if (e.mBitCount >= mBiggestResourceColorDepth &&
  194. entrySize.width * entrySize.height >=
  195. mBiggestResourceSize.width * mBiggestResourceSize.height) {
  196. mBiggestResourceSize = entrySize;
  197. mBiggestResourceColorDepth = e.mBitCount;
  198. mBiggestResourceHotSpot = IntSize(e.mXHotspot, e.mYHotspot);
  199. if (!desiredSize) {
  200. mDirEntry = e;
  201. }
  202. }
  203. if (desiredSize) {
  204. // Calculate the delta between this resource's size and the desired size, so
  205. // we can see if it is better than our current-best option. In the case of
  206. // several equally-good resources, we use the last one. "Better" in this
  207. // case is determined by |delta|, a measure of the difference in size
  208. // between the entry we've found and the desired size. We will choose the
  209. // smallest resource that is greater than or equal to the desired size (i.e.
  210. // we assume it's better to downscale a larger icon than to upscale a
  211. // smaller one).
  212. int32_t delta = std::min(entrySize.width - desiredSize->width,
  213. entrySize.height - desiredSize->height);
  214. if (e.mBitCount >= mBestResourceColorDepth &&
  215. ((mBestResourceDelta < 0 && delta >= mBestResourceDelta) ||
  216. (delta >= 0 && delta <= mBestResourceDelta))) {
  217. mBestResourceDelta = delta;
  218. mBestResourceColorDepth = e.mBitCount;
  219. mDirEntry = e;
  220. }
  221. }
  222. if (mCurrIcon == mNumIcons) {
  223. // Ensure the resource we selected has an offset past the ICO headers.
  224. if (mDirEntry.mImageOffset < FirstResourceOffset()) {
  225. return Transition::TerminateFailure();
  226. }
  227. // If this is a cursor, set the hotspot. We use the hotspot from the biggest
  228. // resource since we also use that resource for the intrinsic size.
  229. if (mIsCursor) {
  230. mImageMetadata.SetHotspot(mBiggestResourceHotSpot.width,
  231. mBiggestResourceHotSpot.height);
  232. }
  233. // We always report the biggest resource's size as the intrinsic size; this
  234. // is necessary for downscale-during-decode to work since we won't even
  235. // attempt to *upscale* while decoding.
  236. PostSize(mBiggestResourceSize.width, mBiggestResourceSize.height);
  237. if (IsMetadataDecode()) {
  238. return Transition::TerminateSuccess();
  239. }
  240. // If the resource we selected matches the output size perfectly, we don't
  241. // need to do any downscaling.
  242. if (GetRealSize() == OutputSize()) {
  243. MOZ_ASSERT_IF(desiredSize, GetRealSize() == *desiredSize);
  244. MOZ_ASSERT_IF(!desiredSize, GetRealSize() == Size());
  245. mDownscaler.reset();
  246. }
  247. size_t offsetToResource = mDirEntry.mImageOffset - FirstResourceOffset();
  248. return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
  249. ICOState::SKIP_TO_RESOURCE,
  250. offsetToResource);
  251. }
  252. return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
  253. }
  254. LexerTransition<ICOState>
  255. nsICODecoder::SniffResource(const char* aData)
  256. {
  257. // We use the first PNGSIGNATURESIZE bytes to determine whether this resource
  258. // is a PNG or a BMP.
  259. bool isPNG = !memcmp(aData, nsPNGDecoder::pngSignatureBytes,
  260. PNGSIGNATURESIZE);
  261. if (isPNG) {
  262. // Create a PNG decoder which will do the rest of the work for us.
  263. mContainedSourceBuffer = new SourceBuffer();
  264. mContainedSourceBuffer->ExpectLength(mDirEntry.mBytesInRes);
  265. mContainedDecoder =
  266. DecoderFactory::CreateDecoderForICOResource(DecoderType::PNG,
  267. WrapNotNull(mContainedSourceBuffer),
  268. WrapNotNull(this));
  269. if (!WriteToContainedDecoder(aData, PNGSIGNATURESIZE)) {
  270. return Transition::TerminateFailure();
  271. }
  272. if (mDirEntry.mBytesInRes <= PNGSIGNATURESIZE) {
  273. return Transition::TerminateFailure();
  274. }
  275. // Read in the rest of the PNG unbuffered.
  276. size_t toRead = mDirEntry.mBytesInRes - PNGSIGNATURESIZE;
  277. return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
  278. ICOState::READ_PNG,
  279. toRead);
  280. } else {
  281. // Make sure we have a sane size for the bitmap information header.
  282. int32_t bihSize = LittleEndian::readUint32(aData);
  283. if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
  284. return Transition::TerminateFailure();
  285. }
  286. // Buffer the first part of the bitmap information header.
  287. memcpy(mBIHraw, aData, PNGSIGNATURESIZE);
  288. // Read in the rest of the bitmap information header.
  289. return Transition::To(ICOState::READ_BIH,
  290. BITMAPINFOSIZE - PNGSIGNATURESIZE);
  291. }
  292. }
  293. LexerTransition<ICOState>
  294. nsICODecoder::ReadPNG(const char* aData, uint32_t aLen)
  295. {
  296. if (!WriteToContainedDecoder(aData, aLen)) {
  297. return Transition::TerminateFailure();
  298. }
  299. // Raymond Chen says that 32bpp only are valid PNG ICOs
  300. // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
  301. if (!static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) {
  302. return Transition::TerminateFailure();
  303. }
  304. return Transition::ContinueUnbuffered(ICOState::READ_PNG);
  305. }
  306. LexerTransition<ICOState>
  307. nsICODecoder::ReadBIH(const char* aData)
  308. {
  309. // Buffer the rest of the bitmap information header.
  310. memcpy(mBIHraw + PNGSIGNATURESIZE, aData, BITMAPINFOSIZE - PNGSIGNATURESIZE);
  311. // Extract the BPP from the BIH header; it should be trusted over the one
  312. // we have from the ICO header which is usually set to 0.
  313. mBPP = LittleEndian::readUint16(mBIHraw + 14);
  314. // The ICO format when containing a BMP does not include the 14 byte
  315. // bitmap file header. So we create the BMP decoder via the constructor that
  316. // tells it to skip this, and pass in the required data (dataOffset) that
  317. // would have been present in the header.
  318. uint32_t dataOffset = bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE;
  319. if (mDirEntry.mBitCount <= 8) {
  320. // The color table is present only if BPP is <= 8.
  321. uint16_t numColors = GetNumColors();
  322. if (numColors == (uint16_t)-1) {
  323. return Transition::TerminateFailure();
  324. }
  325. dataOffset += 4 * numColors;
  326. }
  327. // Create a BMP decoder which will do most of the work for us; the exception
  328. // is the AND mask, which isn't present in standalone BMPs.
  329. mContainedSourceBuffer = new SourceBuffer();
  330. mContainedSourceBuffer->ExpectLength(mDirEntry.mBytesInRes);
  331. mContainedDecoder =
  332. DecoderFactory::CreateDecoderForICOResource(DecoderType::BMP,
  333. WrapNotNull(mContainedSourceBuffer),
  334. WrapNotNull(this),
  335. Some(dataOffset));
  336. RefPtr<nsBMPDecoder> bmpDecoder =
  337. static_cast<nsBMPDecoder*>(mContainedDecoder.get());
  338. // Verify that the BIH width and height values match the ICO directory entry,
  339. // and fix the BIH height value to compensate for the fact that the underlying
  340. // BMP decoder doesn't know about AND masks.
  341. if (!CheckAndFixBitmapSize(reinterpret_cast<int8_t*>(mBIHraw))) {
  342. return Transition::TerminateFailure();
  343. }
  344. // Write out the BMP's bitmap info header.
  345. if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw))) {
  346. return Transition::TerminateFailure();
  347. }
  348. // Check to make sure we have valid color settings.
  349. uint16_t numColors = GetNumColors();
  350. if (numColors == uint16_t(-1)) {
  351. return Transition::TerminateFailure();
  352. }
  353. // Do we have an AND mask on this BMP? If so, we need to read it after we read
  354. // the BMP data itself.
  355. uint32_t bmpDataLength = bmpDecoder->GetCompressedImageSize() + 4 * numColors;
  356. bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry.mBytesInRes;
  357. ICOState afterBMPState = hasANDMask ? ICOState::PREPARE_FOR_MASK
  358. : ICOState::FINISHED_RESOURCE;
  359. // Read in the rest of the BMP unbuffered.
  360. return Transition::ToUnbuffered(afterBMPState,
  361. ICOState::READ_BMP,
  362. bmpDataLength);
  363. }
  364. LexerTransition<ICOState>
  365. nsICODecoder::ReadBMP(const char* aData, uint32_t aLen)
  366. {
  367. if (!WriteToContainedDecoder(aData, aLen)) {
  368. return Transition::TerminateFailure();
  369. }
  370. return Transition::ContinueUnbuffered(ICOState::READ_BMP);
  371. }
  372. LexerTransition<ICOState>
  373. nsICODecoder::PrepareForMask()
  374. {
  375. RefPtr<nsBMPDecoder> bmpDecoder =
  376. static_cast<nsBMPDecoder*>(mContainedDecoder.get());
  377. uint16_t numColors = GetNumColors();
  378. MOZ_ASSERT(numColors != uint16_t(-1));
  379. // Determine the length of the AND mask.
  380. uint32_t bmpLengthWithHeader =
  381. BITMAPINFOSIZE + bmpDecoder->GetCompressedImageSize() + 4 * numColors;
  382. MOZ_ASSERT(bmpLengthWithHeader < mDirEntry.mBytesInRes);
  383. uint32_t maskLength = mDirEntry.mBytesInRes - bmpLengthWithHeader;
  384. // If the BMP provides its own transparency, we ignore the AND mask. We can
  385. // also obviously ignore it if the image has zero width or zero height.
  386. if (bmpDecoder->HasTransparency() ||
  387. GetRealWidth() == 0 || GetRealHeight() == 0) {
  388. return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
  389. ICOState::SKIP_MASK,
  390. maskLength);
  391. }
  392. // Compute the row size for the mask.
  393. mMaskRowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
  394. // If the expected size of the AND mask is larger than its actual size, then
  395. // we must have a truncated (and therefore corrupt) AND mask.
  396. uint32_t expectedLength = mMaskRowSize * GetRealHeight();
  397. if (maskLength < expectedLength) {
  398. return Transition::TerminateFailure();
  399. }
  400. // If we're downscaling, the mask is the wrong size for the surface we've
  401. // produced, so we need to downscale the mask into a temporary buffer and then
  402. // combine the mask's alpha values with the color values from the image.
  403. if (mDownscaler) {
  404. MOZ_ASSERT(bmpDecoder->GetImageDataLength() ==
  405. mDownscaler->TargetSize().width *
  406. mDownscaler->TargetSize().height *
  407. sizeof(uint32_t));
  408. mMaskBuffer = MakeUnique<uint8_t[]>(bmpDecoder->GetImageDataLength());
  409. nsresult rv = mDownscaler->BeginFrame(GetRealSize(), Nothing(),
  410. mMaskBuffer.get(),
  411. /* aHasAlpha = */ true,
  412. /* aFlipVertically = */ true);
  413. if (NS_FAILED(rv)) {
  414. return Transition::TerminateFailure();
  415. }
  416. }
  417. mCurrMaskLine = GetRealHeight();
  418. return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
  419. }
  420. LexerTransition<ICOState>
  421. nsICODecoder::ReadMaskRow(const char* aData)
  422. {
  423. mCurrMaskLine--;
  424. uint8_t sawTransparency = 0;
  425. // Get the mask row we're reading.
  426. const uint8_t* mask = reinterpret_cast<const uint8_t*>(aData);
  427. const uint8_t* maskRowEnd = mask + mMaskRowSize;
  428. // Get the corresponding row of the mask buffer (if we're downscaling) or the
  429. // decoded image data (if we're not).
  430. uint32_t* decoded = nullptr;
  431. if (mDownscaler) {
  432. // Initialize the row to all white and fully opaque.
  433. memset(mDownscaler->RowBuffer(), 0xFF, GetRealWidth() * sizeof(uint32_t));
  434. decoded = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
  435. } else {
  436. RefPtr<nsBMPDecoder> bmpDecoder =
  437. static_cast<nsBMPDecoder*>(mContainedDecoder.get());
  438. uint32_t* imageData = bmpDecoder->GetImageData();
  439. if (!imageData) {
  440. return Transition::TerminateFailure();
  441. }
  442. decoded = imageData + mCurrMaskLine * GetRealWidth();
  443. }
  444. MOZ_ASSERT(decoded);
  445. uint32_t* decodedRowEnd = decoded + GetRealWidth();
  446. // Iterate simultaneously through the AND mask and the image data.
  447. while (mask < maskRowEnd) {
  448. uint8_t idx = *mask++;
  449. sawTransparency |= idx;
  450. for (uint8_t bit = 0x80; bit && decoded < decodedRowEnd; bit >>= 1) {
  451. // Clear pixel completely for transparency.
  452. if (idx & bit) {
  453. *decoded = 0;
  454. }
  455. decoded++;
  456. }
  457. }
  458. if (mDownscaler) {
  459. mDownscaler->CommitRow();
  460. }
  461. // If any bits are set in sawTransparency, then we know at least one pixel was
  462. // transparent.
  463. if (sawTransparency) {
  464. mHasMaskAlpha = true;
  465. }
  466. if (mCurrMaskLine == 0) {
  467. return Transition::To(ICOState::FINISH_MASK, 0);
  468. }
  469. return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
  470. }
  471. LexerTransition<ICOState>
  472. nsICODecoder::FinishMask()
  473. {
  474. // If we're downscaling, we now have the appropriate alpha values in
  475. // mMaskBuffer. We just need to transfer them to the image.
  476. if (mDownscaler) {
  477. // Retrieve the image data.
  478. RefPtr<nsBMPDecoder> bmpDecoder =
  479. static_cast<nsBMPDecoder*>(mContainedDecoder.get());
  480. uint8_t* imageData = reinterpret_cast<uint8_t*>(bmpDecoder->GetImageData());
  481. if (!imageData) {
  482. return Transition::TerminateFailure();
  483. }
  484. // Iterate through the alpha values, copying from mask to image.
  485. MOZ_ASSERT(mMaskBuffer);
  486. MOZ_ASSERT(bmpDecoder->GetImageDataLength() > 0);
  487. for (size_t i = 3 ; i < bmpDecoder->GetImageDataLength() ; i += 4) {
  488. imageData[i] = mMaskBuffer[i];
  489. }
  490. }
  491. return Transition::To(ICOState::FINISHED_RESOURCE, 0);
  492. }
  493. LexerTransition<ICOState>
  494. nsICODecoder::FinishResource()
  495. {
  496. // Make sure the actual size of the resource matches the size in the directory
  497. // entry. If not, we consider the image corrupt.
  498. if (mContainedDecoder->HasSize() &&
  499. mContainedDecoder->Size() != GetRealSize()) {
  500. return Transition::TerminateFailure();
  501. }
  502. return Transition::TerminateSuccess();
  503. }
  504. LexerResult
  505. nsICODecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
  506. {
  507. MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
  508. return mLexer.Lex(aIterator, aOnResume,
  509. [=](ICOState aState, const char* aData, size_t aLength) {
  510. switch (aState) {
  511. case ICOState::HEADER:
  512. return ReadHeader(aData);
  513. case ICOState::DIR_ENTRY:
  514. return ReadDirEntry(aData);
  515. case ICOState::SKIP_TO_RESOURCE:
  516. return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
  517. case ICOState::FOUND_RESOURCE:
  518. return Transition::To(ICOState::SNIFF_RESOURCE, PNGSIGNATURESIZE);
  519. case ICOState::SNIFF_RESOURCE:
  520. return SniffResource(aData);
  521. case ICOState::READ_PNG:
  522. return ReadPNG(aData, aLength);
  523. case ICOState::READ_BIH:
  524. return ReadBIH(aData);
  525. case ICOState::READ_BMP:
  526. return ReadBMP(aData, aLength);
  527. case ICOState::PREPARE_FOR_MASK:
  528. return PrepareForMask();
  529. case ICOState::READ_MASK_ROW:
  530. return ReadMaskRow(aData);
  531. case ICOState::FINISH_MASK:
  532. return FinishMask();
  533. case ICOState::SKIP_MASK:
  534. return Transition::ContinueUnbuffered(ICOState::SKIP_MASK);
  535. case ICOState::FINISHED_RESOURCE:
  536. return FinishResource();
  537. default:
  538. MOZ_CRASH("Unknown ICOState");
  539. }
  540. });
  541. }
  542. bool
  543. nsICODecoder::WriteToContainedDecoder(const char* aBuffer, uint32_t aCount)
  544. {
  545. MOZ_ASSERT(mContainedDecoder);
  546. MOZ_ASSERT(mContainedSourceBuffer);
  547. // Append the provided data to the SourceBuffer that the contained decoder is
  548. // reading from.
  549. mContainedSourceBuffer->Append(aBuffer, aCount);
  550. bool succeeded = true;
  551. // Write to the contained decoder. If we run out of data, the ICO decoder will
  552. // get resumed when there's more data available, as usual, so we don't need
  553. // the contained decoder to get resumed too. To avoid that, we provide an
  554. // IResumable which just does nothing.
  555. LexerResult result = mContainedDecoder->Decode();
  556. if (result == LexerResult(TerminalState::FAILURE)) {
  557. succeeded = false;
  558. }
  559. MOZ_ASSERT(result != LexerResult(Yield::OUTPUT_AVAILABLE),
  560. "Unexpected yield");
  561. // Make our state the same as the state of the contained decoder, and
  562. // propagate errors.
  563. mProgress |= mContainedDecoder->TakeProgress();
  564. mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
  565. if (mContainedDecoder->HasError()) {
  566. succeeded = false;
  567. }
  568. return succeeded;
  569. }
  570. } // namespace image
  571. } // namespace mozilla