Backed out 3 changesets (bug 1892041) for causing SM failures in test262. CLOSED...
[gecko.git] / image / decoders / nsICODecoder.cpp
blobff37355429e8a195eeda54261696251fef2a723f
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/. */
6 /* This is a Cross-Platform ICO Decoder, which should work everywhere, including
7 * Big-Endian machines like the PowerPC. */
9 #include "nsICODecoder.h"
11 #include <stdlib.h>
13 #include <utility>
15 #include "RasterImage.h"
16 #include "mozilla/EndianUtils.h"
17 #include "mozilla/gfx/Swizzle.h"
18 #include "mozilla/UniquePtrExtensions.h"
20 using namespace mozilla::gfx;
22 namespace mozilla {
23 namespace image {
25 // Constants.
26 static const uint32_t ICOHEADERSIZE = 6;
27 static const uint32_t BITMAPINFOSIZE = bmp::InfoHeaderLength::WIN_ICO;
29 // ----------------------------------------
30 // Actual Data Processing
31 // ----------------------------------------
33 // Obtains the number of colors from the bits per pixel
34 uint16_t nsICODecoder::GetNumColors() {
35 uint16_t numColors = 0;
36 if (mBPP <= 8) {
37 switch (mBPP) {
38 case 1:
39 numColors = 2;
40 break;
41 case 4:
42 numColors = 16;
43 break;
44 case 8:
45 numColors = 256;
46 break;
47 default:
48 numColors = (uint16_t)-1;
51 return numColors;
54 nsICODecoder::nsICODecoder(RasterImage* aImage)
55 : Decoder(aImage),
56 mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE),
57 Transition::TerminateSuccess()),
58 mDirEntry(nullptr),
59 mNumIcons(0),
60 mCurrIcon(0),
61 mBPP(0),
62 mMaskRowSize(0),
63 mCurrMaskLine(0),
64 mIsCursor(false),
65 mHasMaskAlpha(false) {}
67 nsresult nsICODecoder::FinishInternal() {
68 // We shouldn't be called in error cases
69 MOZ_ASSERT(!HasError(), "Shouldn't call FinishInternal after error!");
71 return GetFinalStateFromContainedDecoder();
74 nsresult nsICODecoder::FinishWithErrorInternal() {
75 // No need to assert !mInFrame here because this condition is enforced by
76 // mContainedDecoder.
77 return GetFinalStateFromContainedDecoder();
80 nsresult nsICODecoder::GetFinalStateFromContainedDecoder() {
81 if (!mContainedDecoder) {
82 return NS_OK;
85 // Let the contained decoder finish up if necessary.
86 FlushContainedDecoder();
88 // Make our state the same as the state of the contained decoder.
89 mDecodeDone = mContainedDecoder->GetDecodeDone();
90 mProgress |= mContainedDecoder->TakeProgress();
91 mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
92 mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
94 // Finalize the frame which we deferred to ensure we could modify the final
95 // result (e.g. to apply the BMP mask).
96 MOZ_ASSERT(!mContainedDecoder->GetFinalizeFrames());
97 if (mCurrentFrame) {
98 mCurrentFrame->FinalizeSurface();
101 // Propagate errors.
102 nsresult rv =
103 HasError() || mContainedDecoder->HasError() ? NS_ERROR_FAILURE : NS_OK;
105 MOZ_ASSERT(NS_FAILED(rv) || !mCurrentFrame || mCurrentFrame->IsFinished());
106 return rv;
109 LexerTransition<ICOState> nsICODecoder::ReadHeader(const char* aData) {
110 // If the third byte is 1, this is an icon. If 2, a cursor.
111 if ((aData[2] != 1) && (aData[2] != 2)) {
112 return Transition::TerminateFailure();
114 mIsCursor = (aData[2] == 2);
116 // The fifth and sixth bytes specify the number of resources in the file.
117 mNumIcons = LittleEndian::readUint16(aData + 4);
118 if (mNumIcons == 0) {
119 return Transition::TerminateSuccess(); // Nothing to do.
122 // Downscale-during-decode can end up decoding different resources in the ICO
123 // file depending on the target size. Since the resources are not necessarily
124 // scaled versions of the same image, some may be transparent and some may not
125 // be. We could be precise about transparency if we decoded the metadata of
126 // every resource, but for now we don't and it's safest to assume that
127 // transparency could be present.
128 PostHasTransparency();
130 return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
133 size_t nsICODecoder::FirstResourceOffset() const {
134 MOZ_ASSERT(mNumIcons > 0,
135 "Calling FirstResourceOffset before processing header");
137 // The first resource starts right after the directory, which starts right
138 // after the ICO header.
139 return ICOHEADERSIZE + mNumIcons * ICODIRENTRYSIZE;
142 LexerTransition<ICOState> nsICODecoder::ReadDirEntry(const char* aData) {
143 mCurrIcon++;
145 // Ensure the resource has an offset past the ICO headers.
146 uint32_t offset = LittleEndian::readUint32(aData + 12);
147 if (offset >= FirstResourceOffset()) {
148 // Read the directory entry.
149 IconDirEntryEx e;
150 e.mWidth = aData[0];
151 e.mHeight = aData[1];
152 e.mColorCount = aData[2];
153 e.mReserved = aData[3];
154 e.mPlanes = LittleEndian::readUint16(aData + 4);
155 e.mBitCount = LittleEndian::readUint16(aData + 6);
156 e.mBytesInRes = LittleEndian::readUint32(aData + 8);
157 e.mImageOffset = offset;
158 e.mSize = OrientedIntSize(e.mWidth, e.mHeight);
160 // Only accept entries with sufficient resource data to actually contain
161 // some image data.
162 if (e.mBytesInRes > BITMAPINFOSIZE) {
163 if (e.mWidth == 0 || e.mHeight == 0) {
164 mUnsizedDirEntries.AppendElement(e);
165 } else {
166 mDirEntries.AppendElement(e);
171 if (mCurrIcon == mNumIcons) {
172 if (mUnsizedDirEntries.IsEmpty()) {
173 return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
175 return Transition::To(ICOState::ITERATE_UNSIZED_DIR_ENTRY, 0);
178 return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
181 LexerTransition<ICOState> nsICODecoder::IterateUnsizedDirEntry() {
182 MOZ_ASSERT(!mUnsizedDirEntries.IsEmpty());
184 if (!mDirEntry) {
185 // The first time we are here, there is no entry selected. We must prepare a
186 // new iterator for the contained decoder to advance as it wills. Cloning at
187 // this point ensures it will begin at the end of the dir entries.
188 mReturnIterator = mLexer.Clone(*mIterator, SIZE_MAX);
189 if (mReturnIterator.isNothing()) {
190 // If we cannot read further than this point, then there is no resource
191 // data to read.
192 return Transition::TerminateFailure();
194 } else {
195 // We have already selected an entry which means a metadata decoder has
196 // finished. Verify the size is valid and if so, add to the discovered
197 // resources.
198 if (mDirEntry->mSize.width > 0 && mDirEntry->mSize.height > 0) {
199 mDirEntries.AppendElement(*mDirEntry);
202 // Remove the entry from the unsized list either way.
203 mDirEntry = nullptr;
204 mUnsizedDirEntries.RemoveElementAt(0);
206 // Our iterator is at an unknown point, so reset it to the point that we
207 // saved.
208 mIterator = mLexer.Clone(*mReturnIterator, SIZE_MAX);
209 if (mIterator.isNothing()) {
210 MOZ_ASSERT_UNREACHABLE("Cannot re-clone return iterator");
211 return Transition::TerminateFailure();
215 // There are no more unsized entries, so we can finally decide which entry to
216 // select for decoding.
217 if (mUnsizedDirEntries.IsEmpty()) {
218 mReturnIterator.reset();
219 return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
222 // Move to the resource data to start metadata decoding.
223 mDirEntry = &mUnsizedDirEntries[0];
224 size_t offsetToResource = mDirEntry->mImageOffset - FirstResourceOffset();
225 return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
226 ICOState::SKIP_TO_RESOURCE, offsetToResource);
229 LexerTransition<ICOState> nsICODecoder::FinishDirEntry() {
230 MOZ_ASSERT(!mDirEntry);
232 if (mDirEntries.IsEmpty()) {
233 return Transition::TerminateFailure();
236 // If an explicit output size was specified, we'll try to select the resource
237 // that matches it best below.
238 const Maybe<OrientedIntSize> desiredSize = ExplicitOutputSize();
240 // Determine the biggest resource. We always use the biggest resource for the
241 // intrinsic size, and if we don't have a specific desired size, we select it
242 // as the best resource as well.
243 int32_t bestDelta = INT32_MIN;
244 IconDirEntryEx* biggestEntry = nullptr;
246 for (size_t i = 0; i < mDirEntries.Length(); ++i) {
247 IconDirEntryEx& e = mDirEntries[i];
248 mImageMetadata.AddNativeSize(e.mSize);
250 if (!biggestEntry ||
251 (e.mBitCount >= biggestEntry->mBitCount &&
252 e.mSize.width * e.mSize.height >=
253 biggestEntry->mSize.width * biggestEntry->mSize.height)) {
254 biggestEntry = &e;
256 if (!desiredSize) {
257 mDirEntry = &e;
261 if (desiredSize) {
262 // Calculate the delta between this resource's size and the desired size,
263 // so we can see if it is better than our current-best option. In the
264 // case of several equally-good resources, we use the last one. "Better"
265 // in this case is determined by |delta|, a measure of the difference in
266 // size between the entry we've found and the desired size. We will choose
267 // the smallest resource that is greater than or equal to the desired size
268 // (i.e. we assume it's better to downscale a larger icon than to upscale
269 // a smaller one).
270 int32_t delta = std::min(e.mSize.width - desiredSize->width,
271 e.mSize.height - desiredSize->height);
272 if (!mDirEntry || (e.mBitCount >= mDirEntry->mBitCount &&
273 ((bestDelta < 0 && delta >= bestDelta) ||
274 (delta >= 0 && delta <= bestDelta)))) {
275 mDirEntry = &e;
276 bestDelta = delta;
281 MOZ_ASSERT(mDirEntry);
282 MOZ_ASSERT(biggestEntry);
284 // If this is a cursor, set the hotspot. We use the hotspot from the biggest
285 // resource since we also use that resource for the intrinsic size.
286 if (mIsCursor) {
287 mImageMetadata.SetHotspot(biggestEntry->mXHotspot, biggestEntry->mYHotspot);
290 // We always report the biggest resource's size as the intrinsic size; this
291 // is necessary for downscale-during-decode to work since we won't even
292 // attempt to *upscale* while decoding.
293 PostSize(biggestEntry->mSize.width, biggestEntry->mSize.height);
294 if (HasError()) {
295 return Transition::TerminateFailure();
298 if (IsMetadataDecode()) {
299 return Transition::TerminateSuccess();
302 if (mDirEntry->mSize == OutputSize()) {
303 // If the resource we selected matches the output size perfectly, we don't
304 // need to do any downscaling.
305 MOZ_ASSERT_IF(desiredSize, mDirEntry->mSize == *desiredSize);
306 MOZ_ASSERT_IF(!desiredSize, mDirEntry->mSize == Size());
307 } else if (OutputSize().width < mDirEntry->mSize.width ||
308 OutputSize().height < mDirEntry->mSize.height) {
309 // Create a downscaler if we need to downscale.
311 // TODO(aosmond): This is the last user of Downscaler. We should switch this
312 // to SurfacePipe as well so we can remove the code from tree.
313 mDownscaler.emplace(OutputSize().ToUnknownSize());
316 size_t offsetToResource = mDirEntry->mImageOffset - FirstResourceOffset();
317 return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
318 ICOState::SKIP_TO_RESOURCE, offsetToResource);
321 LexerTransition<ICOState> nsICODecoder::SniffResource(const char* aData) {
322 MOZ_ASSERT(mDirEntry);
324 // We have BITMAPINFOSIZE bytes buffered at this point. We know an embedded
325 // BMP will have at least that many bytes by definition. We can also infer
326 // that any valid embedded PNG will contain that many bytes as well because:
327 // BITMAPINFOSIZE
328 // <
329 // signature (8 bytes) +
330 // IHDR (12 bytes header + 13 bytes data)
331 // IDAT (12 bytes header)
333 // We use the first PNGSIGNATURESIZE bytes to determine whether this resource
334 // is a PNG or a BMP.
335 bool isPNG =
336 !memcmp(aData, nsPNGDecoder::pngSignatureBytes, PNGSIGNATURESIZE);
337 if (isPNG) {
338 if (mDirEntry->mBytesInRes <= BITMAPINFOSIZE) {
339 return Transition::TerminateFailure();
342 // Prepare a new iterator for the contained decoder to advance as it wills.
343 // Cloning at the point ensures it will begin at the resource offset.
344 Maybe<SourceBufferIterator> containedIterator =
345 mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
346 if (containedIterator.isNothing()) {
347 return Transition::TerminateFailure();
350 // Create a PNG decoder which will do the rest of the work for us.
351 bool metadataDecode = mReturnIterator.isSome();
352 Maybe<OrientedIntSize> expectedSize =
353 metadataDecode ? Nothing() : Some(mDirEntry->mSize);
354 mContainedDecoder = DecoderFactory::CreateDecoderForICOResource(
355 DecoderType::PNG, std::move(containedIterator.ref()), WrapNotNull(this),
356 metadataDecode, expectedSize);
358 // Read in the rest of the PNG unbuffered.
359 size_t toRead = mDirEntry->mBytesInRes - BITMAPINFOSIZE;
360 return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
361 ICOState::READ_RESOURCE, toRead);
364 // Make sure we have a sane size for the bitmap information header.
365 int32_t bihSize = LittleEndian::readUint32(aData);
366 if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
367 return Transition::TerminateFailure();
370 // Read in the rest of the bitmap information header.
371 return ReadBIH(aData);
374 LexerTransition<ICOState> nsICODecoder::ReadResource() {
375 if (!FlushContainedDecoder()) {
376 return Transition::TerminateFailure();
379 return Transition::ContinueUnbuffered(ICOState::READ_RESOURCE);
382 LexerTransition<ICOState> nsICODecoder::ReadBIH(const char* aData) {
383 MOZ_ASSERT(mDirEntry);
385 // Extract the BPP from the BIH header; it should be trusted over the one
386 // we have from the ICO header which is usually set to 0.
387 mBPP = LittleEndian::readUint16(aData + 14);
389 // Check to make sure we have valid color settings.
390 uint16_t numColors = GetNumColors();
391 if (numColors == uint16_t(-1)) {
392 return Transition::TerminateFailure();
395 // The color table is present only if BPP is <= 8.
396 MOZ_ASSERT_IF(mBPP > 8, numColors == 0);
398 // The ICO format when containing a BMP does not include the 14 byte
399 // bitmap file header. So we create the BMP decoder via the constructor that
400 // tells it to skip this, and pass in the required data (dataOffset) that
401 // would have been present in the header.
402 uint32_t dataOffset =
403 bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE + 4 * numColors;
405 // Prepare a new iterator for the contained decoder to advance as it wills.
406 // Cloning at the point ensures it will begin at the resource offset.
407 Maybe<SourceBufferIterator> containedIterator =
408 mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
409 if (containedIterator.isNothing()) {
410 return Transition::TerminateFailure();
413 // Create a BMP decoder which will do most of the work for us; the exception
414 // is the AND mask, which isn't present in standalone BMPs.
415 bool metadataDecode = mReturnIterator.isSome();
416 Maybe<OrientedIntSize> expectedSize =
417 metadataDecode ? Nothing() : Some(mDirEntry->mSize);
418 mContainedDecoder = DecoderFactory::CreateDecoderForICOResource(
419 DecoderType::BMP, std::move(containedIterator.ref()), WrapNotNull(this),
420 metadataDecode, expectedSize, Some(dataOffset));
422 RefPtr<nsBMPDecoder> bmpDecoder =
423 static_cast<nsBMPDecoder*>(mContainedDecoder.get());
425 // Ensure the decoder has parsed at least the BMP's bitmap info header.
426 if (!FlushContainedDecoder()) {
427 return Transition::TerminateFailure();
430 // If this is a metadata decode, FinishResource will any necessary checks.
431 if (mContainedDecoder->IsMetadataDecode()) {
432 return Transition::To(ICOState::FINISHED_RESOURCE, 0);
435 // Do we have an AND mask on this BMP? If so, we need to read it after we read
436 // the BMP data itself.
437 uint32_t bmpDataLength = bmpDecoder->GetCompressedImageSize() + 4 * numColors;
438 bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry->mBytesInRes;
439 ICOState afterBMPState =
440 hasANDMask ? ICOState::PREPARE_FOR_MASK : ICOState::FINISHED_RESOURCE;
442 // Read in the rest of the BMP unbuffered.
443 return Transition::ToUnbuffered(afterBMPState, ICOState::READ_RESOURCE,
444 bmpDataLength);
447 LexerTransition<ICOState> nsICODecoder::PrepareForMask() {
448 MOZ_ASSERT(mDirEntry);
449 MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
451 // We have received all of the data required by the BMP decoder so flushing
452 // here guarantees the decode has finished.
453 if (!FlushContainedDecoder()) {
454 return Transition::TerminateFailure();
457 MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
459 RefPtr<nsBMPDecoder> bmpDecoder =
460 static_cast<nsBMPDecoder*>(mContainedDecoder.get());
462 uint16_t numColors = GetNumColors();
463 MOZ_ASSERT(numColors != uint16_t(-1));
465 // Determine the length of the AND mask.
466 uint32_t bmpLengthWithHeader =
467 BITMAPINFOSIZE + bmpDecoder->GetCompressedImageSize() + 4 * numColors;
468 MOZ_ASSERT(bmpLengthWithHeader < mDirEntry->mBytesInRes);
469 uint32_t maskLength = mDirEntry->mBytesInRes - bmpLengthWithHeader;
471 // If the BMP provides its own transparency, we ignore the AND mask.
472 if (bmpDecoder->HasTransparency()) {
473 return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
474 ICOState::SKIP_MASK, maskLength);
477 // Compute the row size for the mask.
478 mMaskRowSize = ((mDirEntry->mSize.width + 31) / 32) * 4; // + 31 to round up
480 // If the expected size of the AND mask is larger than its actual size, then
481 // we must have a truncated (and therefore corrupt) AND mask.
482 uint32_t expectedLength = mMaskRowSize * mDirEntry->mSize.height;
483 if (maskLength < expectedLength) {
484 return Transition::TerminateFailure();
487 // If we're downscaling, the mask is the wrong size for the surface we've
488 // produced, so we need to downscale the mask into a temporary buffer and then
489 // combine the mask's alpha values with the color values from the image.
490 if (mDownscaler) {
491 MOZ_ASSERT(bmpDecoder->GetImageDataLength() ==
492 mDownscaler->TargetSize().width *
493 mDownscaler->TargetSize().height * sizeof(uint32_t));
494 mMaskBuffer =
495 MakeUniqueFallible<uint8_t[]>(bmpDecoder->GetImageDataLength());
496 if (NS_WARN_IF(!mMaskBuffer)) {
497 return Transition::TerminateFailure();
499 nsresult rv = mDownscaler->BeginFrame(mDirEntry->mSize.ToUnknownSize(),
500 Nothing(), mMaskBuffer.get(),
501 /* aHasAlpha = */ true,
502 /* aFlipVertically = */ true);
503 if (NS_FAILED(rv)) {
504 return Transition::TerminateFailure();
508 mCurrMaskLine = mDirEntry->mSize.height;
509 return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
512 LexerTransition<ICOState> nsICODecoder::ReadMaskRow(const char* aData) {
513 MOZ_ASSERT(mDirEntry);
515 mCurrMaskLine--;
517 uint8_t sawTransparency = 0;
519 // Get the mask row we're reading.
520 const uint8_t* mask = reinterpret_cast<const uint8_t*>(aData);
521 const uint8_t* maskRowEnd = mask + mMaskRowSize;
523 // Get the corresponding row of the mask buffer (if we're downscaling) or the
524 // decoded image data (if we're not).
525 uint32_t* decoded = nullptr;
526 if (mDownscaler) {
527 // Initialize the row to all white and fully opaque.
528 memset(mDownscaler->RowBuffer(), 0xFF,
529 mDirEntry->mSize.width * sizeof(uint32_t));
531 decoded = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
532 } else {
533 RefPtr<nsBMPDecoder> bmpDecoder =
534 static_cast<nsBMPDecoder*>(mContainedDecoder.get());
535 uint32_t* imageData = bmpDecoder->GetImageData();
536 if (!imageData) {
537 return Transition::TerminateFailure();
540 decoded = imageData + mCurrMaskLine * mDirEntry->mSize.width;
543 MOZ_ASSERT(decoded);
544 uint32_t* decodedRowEnd = decoded + mDirEntry->mSize.width;
546 // Iterate simultaneously through the AND mask and the image data.
547 while (mask < maskRowEnd) {
548 uint8_t idx = *mask++;
549 sawTransparency |= idx;
550 for (uint8_t bit = 0x80; bit && decoded < decodedRowEnd; bit >>= 1) {
551 // Clear pixel completely for transparency.
552 if (idx & bit) {
553 *decoded = 0;
555 decoded++;
559 if (mDownscaler) {
560 mDownscaler->CommitRow();
563 // If any bits are set in sawTransparency, then we know at least one pixel was
564 // transparent.
565 if (sawTransparency) {
566 mHasMaskAlpha = true;
569 if (mCurrMaskLine == 0) {
570 return Transition::To(ICOState::FINISH_MASK, 0);
573 return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
576 LexerTransition<ICOState> nsICODecoder::FinishMask() {
577 // If we're downscaling, we now have the appropriate alpha values in
578 // mMaskBuffer. We just need to transfer them to the image.
579 if (mDownscaler) {
580 // Retrieve the image data.
581 RefPtr<nsBMPDecoder> bmpDecoder =
582 static_cast<nsBMPDecoder*>(mContainedDecoder.get());
583 uint8_t* imageData = reinterpret_cast<uint8_t*>(bmpDecoder->GetImageData());
584 if (!imageData) {
585 return Transition::TerminateFailure();
588 // Iterate through the alpha values, copying from mask to image.
589 MOZ_ASSERT(mMaskBuffer);
590 MOZ_ASSERT(bmpDecoder->GetImageDataLength() > 0);
591 for (size_t i = 3; i < bmpDecoder->GetImageDataLength(); i += 4) {
592 imageData[i] = mMaskBuffer[i];
594 int32_t stride = mDownscaler->TargetSize().width * sizeof(uint32_t);
595 DebugOnly<bool> ret =
596 // We know the format is OS_RGBA because we always assume bmp's inside
597 // ico's are transparent.
598 PremultiplyData(imageData, stride, SurfaceFormat::OS_RGBA, imageData,
599 stride, SurfaceFormat::OS_RGBA,
600 mDownscaler->TargetSize());
601 MOZ_ASSERT(ret);
604 return Transition::To(ICOState::FINISHED_RESOURCE, 0);
607 LexerTransition<ICOState> nsICODecoder::FinishResource() {
608 MOZ_ASSERT(mDirEntry);
610 // We have received all of the data required by the PNG/BMP decoder so
611 // flushing here guarantees the decode has finished.
612 if (!FlushContainedDecoder()) {
613 return Transition::TerminateFailure();
616 MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
618 // If it is a metadata decode, all we were trying to get was the size
619 // information missing from the dir entry.
620 if (mContainedDecoder->IsMetadataDecode()) {
621 if (mContainedDecoder->HasSize()) {
622 mDirEntry->mSize = mContainedDecoder->Size();
624 return Transition::To(ICOState::ITERATE_UNSIZED_DIR_ENTRY, 0);
627 // Raymond Chen says that 32bpp only are valid PNG ICOs
628 // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
629 if (!mContainedDecoder->IsValidICOResource()) {
630 return Transition::TerminateFailure();
633 // This size from the resource should match that from the dir entry.
634 MOZ_ASSERT_IF(mContainedDecoder->HasSize(),
635 mContainedDecoder->Size() == mDirEntry->mSize);
637 return Transition::TerminateSuccess();
640 LexerResult nsICODecoder::DoDecode(SourceBufferIterator& aIterator,
641 IResumable* aOnResume) {
642 MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
644 return mLexer.Lex(
645 aIterator, aOnResume,
646 [=](ICOState aState, const char* aData, size_t aLength) {
647 switch (aState) {
648 case ICOState::HEADER:
649 return ReadHeader(aData);
650 case ICOState::DIR_ENTRY:
651 return ReadDirEntry(aData);
652 case ICOState::FINISHED_DIR_ENTRY:
653 return FinishDirEntry();
654 case ICOState::ITERATE_UNSIZED_DIR_ENTRY:
655 return IterateUnsizedDirEntry();
656 case ICOState::SKIP_TO_RESOURCE:
657 return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
658 case ICOState::FOUND_RESOURCE:
659 return Transition::To(ICOState::SNIFF_RESOURCE, BITMAPINFOSIZE);
660 case ICOState::SNIFF_RESOURCE:
661 return SniffResource(aData);
662 case ICOState::READ_RESOURCE:
663 return ReadResource();
664 case ICOState::PREPARE_FOR_MASK:
665 return PrepareForMask();
666 case ICOState::READ_MASK_ROW:
667 return ReadMaskRow(aData);
668 case ICOState::FINISH_MASK:
669 return FinishMask();
670 case ICOState::SKIP_MASK:
671 return Transition::ContinueUnbuffered(ICOState::SKIP_MASK);
672 case ICOState::FINISHED_RESOURCE:
673 return FinishResource();
674 default:
675 MOZ_CRASH("Unknown ICOState");
680 bool nsICODecoder::FlushContainedDecoder() {
681 MOZ_ASSERT(mContainedDecoder);
683 bool succeeded = true;
685 // If we run out of data, the ICO decoder will get resumed when there's more
686 // data available, as usual, so we don't need the contained decoder to get
687 // resumed too. To avoid that, we provide an IResumable which just does
688 // nothing. All the caller needs to do is flush when there is new data.
689 LexerResult result = mContainedDecoder->Decode();
690 if (result == LexerResult(TerminalState::FAILURE)) {
691 succeeded = false;
694 MOZ_ASSERT(result != LexerResult(Yield::OUTPUT_AVAILABLE),
695 "Unexpected yield");
697 // Make our state the same as the state of the contained decoder, and
698 // propagate errors.
699 mProgress |= mContainedDecoder->TakeProgress();
700 mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
701 if (mContainedDecoder->HasError()) {
702 succeeded = false;
705 return succeeded;
708 } // namespace image
709 } // namespace mozilla