Bug 1689243 [wpt PR 27362] - Reland "Element reflection implementation updates."...
[gecko.git] / image / decoders / nsICODecoder.cpp
blobfe30a092d4a9a3ad8abd9aa6e1c77c4930b04cb0
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"
19 using namespace mozilla::gfx;
21 namespace mozilla {
22 namespace image {
24 // Constants.
25 static const uint32_t ICOHEADERSIZE = 6;
26 static const uint32_t BITMAPINFOSIZE = bmp::InfoHeaderLength::WIN_ICO;
28 // ----------------------------------------
29 // Actual Data Processing
30 // ----------------------------------------
32 // Obtains the number of colors from the bits per pixel
33 uint16_t nsICODecoder::GetNumColors() {
34 uint16_t numColors = 0;
35 if (mBPP <= 8) {
36 switch (mBPP) {
37 case 1:
38 numColors = 2;
39 break;
40 case 4:
41 numColors = 16;
42 break;
43 case 8:
44 numColors = 256;
45 break;
46 default:
47 numColors = (uint16_t)-1;
50 return numColors;
53 nsICODecoder::nsICODecoder(RasterImage* aImage)
54 : Decoder(aImage),
55 mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE),
56 Transition::TerminateSuccess()),
57 mDirEntry(nullptr),
58 mNumIcons(0),
59 mCurrIcon(0),
60 mBPP(0),
61 mMaskRowSize(0),
62 mCurrMaskLine(0),
63 mIsCursor(false),
64 mHasMaskAlpha(false) {}
66 nsresult nsICODecoder::FinishInternal() {
67 // We shouldn't be called in error cases
68 MOZ_ASSERT(!HasError(), "Shouldn't call FinishInternal after error!");
70 return GetFinalStateFromContainedDecoder();
73 nsresult nsICODecoder::FinishWithErrorInternal() {
74 // No need to assert !mInFrame here because this condition is enforced by
75 // mContainedDecoder.
76 return GetFinalStateFromContainedDecoder();
79 nsresult nsICODecoder::GetFinalStateFromContainedDecoder() {
80 if (!mContainedDecoder) {
81 return NS_OK;
84 // Let the contained decoder finish up if necessary.
85 FlushContainedDecoder();
87 // Make our state the same as the state of the contained decoder.
88 mDecodeDone = mContainedDecoder->GetDecodeDone();
89 mProgress |= mContainedDecoder->TakeProgress();
90 mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
91 mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
93 // Finalize the frame which we deferred to ensure we could modify the final
94 // result (e.g. to apply the BMP mask).
95 MOZ_ASSERT(!mContainedDecoder->GetFinalizeFrames());
96 if (mCurrentFrame) {
97 mCurrentFrame->FinalizeSurface();
100 // Propagate errors.
101 nsresult rv =
102 HasError() || mContainedDecoder->HasError() ? NS_ERROR_FAILURE : NS_OK;
104 MOZ_ASSERT(NS_FAILED(rv) || !mCurrentFrame || mCurrentFrame->IsFinished());
105 return rv;
108 LexerTransition<ICOState> nsICODecoder::ReadHeader(const char* aData) {
109 // If the third byte is 1, this is an icon. If 2, a cursor.
110 if ((aData[2] != 1) && (aData[2] != 2)) {
111 return Transition::TerminateFailure();
113 mIsCursor = (aData[2] == 2);
115 // The fifth and sixth bytes specify the number of resources in the file.
116 mNumIcons = LittleEndian::readUint16(aData + 4);
117 if (mNumIcons == 0) {
118 return Transition::TerminateSuccess(); // Nothing to do.
121 // Downscale-during-decode can end up decoding different resources in the ICO
122 // file depending on the target size. Since the resources are not necessarily
123 // scaled versions of the same image, some may be transparent and some may not
124 // be. We could be precise about transparency if we decoded the metadata of
125 // every resource, but for now we don't and it's safest to assume that
126 // transparency could be present.
127 PostHasTransparency();
129 return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
132 size_t nsICODecoder::FirstResourceOffset() const {
133 MOZ_ASSERT(mNumIcons > 0,
134 "Calling FirstResourceOffset before processing header");
136 // The first resource starts right after the directory, which starts right
137 // after the ICO header.
138 return ICOHEADERSIZE + mNumIcons * ICODIRENTRYSIZE;
141 LexerTransition<ICOState> nsICODecoder::ReadDirEntry(const char* aData) {
142 mCurrIcon++;
144 // Ensure the resource has an offset past the ICO headers.
145 uint32_t offset = LittleEndian::readUint32(aData + 12);
146 if (offset >= FirstResourceOffset()) {
147 // Read the directory entry.
148 IconDirEntryEx e;
149 e.mWidth = aData[0];
150 e.mHeight = aData[1];
151 e.mColorCount = aData[2];
152 e.mReserved = aData[3];
153 e.mPlanes = LittleEndian::readUint16(aData + 4);
154 e.mBitCount = LittleEndian::readUint16(aData + 6);
155 e.mBytesInRes = LittleEndian::readUint32(aData + 8);
156 e.mImageOffset = offset;
157 e.mSize = IntSize(e.mWidth, e.mHeight);
159 // Only accept entries with sufficient resource data to actually contain
160 // some image data.
161 if (e.mBytesInRes > BITMAPINFOSIZE) {
162 if (e.mWidth == 0 || e.mHeight == 0) {
163 mUnsizedDirEntries.AppendElement(e);
164 } else {
165 mDirEntries.AppendElement(e);
170 if (mCurrIcon == mNumIcons) {
171 if (mUnsizedDirEntries.IsEmpty()) {
172 return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
174 return Transition::To(ICOState::ITERATE_UNSIZED_DIR_ENTRY, 0);
177 return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
180 LexerTransition<ICOState> nsICODecoder::IterateUnsizedDirEntry() {
181 MOZ_ASSERT(!mUnsizedDirEntries.IsEmpty());
183 if (!mDirEntry) {
184 // The first time we are here, there is no entry selected. We must prepare a
185 // new iterator for the contained decoder to advance as it wills. Cloning at
186 // this point ensures it will begin at the end of the dir entries.
187 mReturnIterator = mLexer.Clone(*mIterator, SIZE_MAX);
188 if (mReturnIterator.isNothing()) {
189 // If we cannot read further than this point, then there is no resource
190 // data to read.
191 return Transition::TerminateFailure();
193 } else {
194 // We have already selected an entry which means a metadata decoder has
195 // finished. Verify the size is valid and if so, add to the discovered
196 // resources.
197 if (mDirEntry->mSize.width > 0 && mDirEntry->mSize.height > 0) {
198 mDirEntries.AppendElement(*mDirEntry);
201 // Remove the entry from the unsized list either way.
202 mDirEntry = nullptr;
203 mUnsizedDirEntries.RemoveElementAt(0);
205 // Our iterator is at an unknown point, so reset it to the point that we
206 // saved.
207 mIterator = mLexer.Clone(*mReturnIterator, SIZE_MAX);
208 if (mIterator.isNothing()) {
209 MOZ_ASSERT_UNREACHABLE("Cannot re-clone return iterator");
210 return Transition::TerminateFailure();
214 // There are no more unsized entries, so we can finally decide which entry to
215 // select for decoding.
216 if (mUnsizedDirEntries.IsEmpty()) {
217 mReturnIterator.reset();
218 return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
221 // Move to the resource data to start metadata decoding.
222 mDirEntry = &mUnsizedDirEntries[0];
223 size_t offsetToResource = mDirEntry->mImageOffset - FirstResourceOffset();
224 return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
225 ICOState::SKIP_TO_RESOURCE, offsetToResource);
228 LexerTransition<ICOState> nsICODecoder::FinishDirEntry() {
229 MOZ_ASSERT(!mDirEntry);
231 if (mDirEntries.IsEmpty()) {
232 return Transition::TerminateFailure();
235 // If an explicit output size was specified, we'll try to select the resource
236 // that matches it best below.
237 const Maybe<IntSize> desiredSize = ExplicitOutputSize();
239 // Determine the biggest resource. We always use the biggest resource for the
240 // intrinsic size, and if we don't have a specific desired size, we select it
241 // as the best resource as well.
242 int32_t bestDelta = INT32_MIN;
243 IconDirEntryEx* biggestEntry = nullptr;
245 for (size_t i = 0; i < mDirEntries.Length(); ++i) {
246 IconDirEntryEx& e = mDirEntries[i];
247 mImageMetadata.AddNativeSize(e.mSize);
249 if (!biggestEntry ||
250 (e.mBitCount >= biggestEntry->mBitCount &&
251 e.mSize.width * e.mSize.height >=
252 biggestEntry->mSize.width * biggestEntry->mSize.height)) {
253 biggestEntry = &e;
255 if (!desiredSize) {
256 mDirEntry = &e;
260 if (desiredSize) {
261 // Calculate the delta between this resource's size and the desired size,
262 // so we can see if it is better than our current-best option. In the
263 // case of several equally-good resources, we use the last one. "Better"
264 // in this case is determined by |delta|, a measure of the difference in
265 // size between the entry we've found and the desired size. We will choose
266 // the smallest resource that is greater than or equal to the desired size
267 // (i.e. we assume it's better to downscale a larger icon than to upscale
268 // a smaller one).
269 int32_t delta = std::min(e.mSize.width - desiredSize->width,
270 e.mSize.height - desiredSize->height);
271 if (!mDirEntry || (e.mBitCount >= mDirEntry->mBitCount &&
272 ((bestDelta < 0 && delta >= bestDelta) ||
273 (delta >= 0 && delta <= bestDelta)))) {
274 mDirEntry = &e;
275 bestDelta = delta;
280 MOZ_ASSERT(mDirEntry);
281 MOZ_ASSERT(biggestEntry);
283 // If this is a cursor, set the hotspot. We use the hotspot from the biggest
284 // resource since we also use that resource for the intrinsic size.
285 if (mIsCursor) {
286 mImageMetadata.SetHotspot(biggestEntry->mXHotspot, biggestEntry->mYHotspot);
289 // We always report the biggest resource's size as the intrinsic size; this
290 // is necessary for downscale-during-decode to work since we won't even
291 // attempt to *upscale* while decoding.
292 PostSize(biggestEntry->mSize.width, biggestEntry->mSize.height);
293 if (HasError()) {
294 return Transition::TerminateFailure();
297 if (IsMetadataDecode()) {
298 return Transition::TerminateSuccess();
301 if (mDirEntry->mSize == OutputSize()) {
302 // If the resource we selected matches the output size perfectly, we don't
303 // need to do any downscaling.
304 MOZ_ASSERT_IF(desiredSize, mDirEntry->mSize == *desiredSize);
305 MOZ_ASSERT_IF(!desiredSize, mDirEntry->mSize == Size());
306 } else if (OutputSize().width < mDirEntry->mSize.width ||
307 OutputSize().height < mDirEntry->mSize.height) {
308 // Create a downscaler if we need to downscale.
310 // TODO(aosmond): This is the last user of Downscaler. We should switch this
311 // to SurfacePipe as well so we can remove the code from tree.
312 mDownscaler.emplace(OutputSize());
315 size_t offsetToResource = mDirEntry->mImageOffset - FirstResourceOffset();
316 return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
317 ICOState::SKIP_TO_RESOURCE, offsetToResource);
320 LexerTransition<ICOState> nsICODecoder::SniffResource(const char* aData) {
321 MOZ_ASSERT(mDirEntry);
323 // We have BITMAPINFOSIZE bytes buffered at this point. We know an embedded
324 // BMP will have at least that many bytes by definition. We can also infer
325 // that any valid embedded PNG will contain that many bytes as well because:
326 // BITMAPINFOSIZE
327 // <
328 // signature (8 bytes) +
329 // IHDR (12 bytes header + 13 bytes data)
330 // IDAT (12 bytes header)
332 // We use the first PNGSIGNATURESIZE bytes to determine whether this resource
333 // is a PNG or a BMP.
334 bool isPNG =
335 !memcmp(aData, nsPNGDecoder::pngSignatureBytes, PNGSIGNATURESIZE);
336 if (isPNG) {
337 if (mDirEntry->mBytesInRes <= BITMAPINFOSIZE) {
338 return Transition::TerminateFailure();
341 // Prepare a new iterator for the contained decoder to advance as it wills.
342 // Cloning at the point ensures it will begin at the resource offset.
343 Maybe<SourceBufferIterator> containedIterator =
344 mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
345 if (containedIterator.isNothing()) {
346 return Transition::TerminateFailure();
349 // Create a PNG decoder which will do the rest of the work for us.
350 bool metadataDecode = mReturnIterator.isSome();
351 Maybe<IntSize> expectedSize =
352 metadataDecode ? Nothing() : Some(mDirEntry->mSize);
353 mContainedDecoder = DecoderFactory::CreateDecoderForICOResource(
354 DecoderType::PNG, std::move(containedIterator.ref()), WrapNotNull(this),
355 metadataDecode, expectedSize);
357 // Read in the rest of the PNG unbuffered.
358 size_t toRead = mDirEntry->mBytesInRes - BITMAPINFOSIZE;
359 return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
360 ICOState::READ_RESOURCE, toRead);
363 // Make sure we have a sane size for the bitmap information header.
364 int32_t bihSize = LittleEndian::readUint32(aData);
365 if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
366 return Transition::TerminateFailure();
369 // Read in the rest of the bitmap information header.
370 return ReadBIH(aData);
373 LexerTransition<ICOState> nsICODecoder::ReadResource() {
374 if (!FlushContainedDecoder()) {
375 return Transition::TerminateFailure();
378 return Transition::ContinueUnbuffered(ICOState::READ_RESOURCE);
381 LexerTransition<ICOState> nsICODecoder::ReadBIH(const char* aData) {
382 MOZ_ASSERT(mDirEntry);
384 // Extract the BPP from the BIH header; it should be trusted over the one
385 // we have from the ICO header which is usually set to 0.
386 mBPP = LittleEndian::readUint16(aData + 14);
388 // Check to make sure we have valid color settings.
389 uint16_t numColors = GetNumColors();
390 if (numColors == uint16_t(-1)) {
391 return Transition::TerminateFailure();
394 // The color table is present only if BPP is <= 8.
395 MOZ_ASSERT_IF(mBPP > 8, numColors == 0);
397 // The ICO format when containing a BMP does not include the 14 byte
398 // bitmap file header. So we create the BMP decoder via the constructor that
399 // tells it to skip this, and pass in the required data (dataOffset) that
400 // would have been present in the header.
401 uint32_t dataOffset =
402 bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE + 4 * numColors;
404 // Prepare a new iterator for the contained decoder to advance as it wills.
405 // Cloning at the point ensures it will begin at the resource offset.
406 Maybe<SourceBufferIterator> containedIterator =
407 mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
408 if (containedIterator.isNothing()) {
409 return Transition::TerminateFailure();
412 // Create a BMP decoder which will do most of the work for us; the exception
413 // is the AND mask, which isn't present in standalone BMPs.
414 bool metadataDecode = mReturnIterator.isSome();
415 Maybe<IntSize> expectedSize =
416 metadataDecode ? Nothing() : Some(mDirEntry->mSize);
417 mContainedDecoder = DecoderFactory::CreateDecoderForICOResource(
418 DecoderType::BMP, std::move(containedIterator.ref()), WrapNotNull(this),
419 metadataDecode, expectedSize, Some(dataOffset));
421 RefPtr<nsBMPDecoder> bmpDecoder =
422 static_cast<nsBMPDecoder*>(mContainedDecoder.get());
424 // Ensure the decoder has parsed at least the BMP's bitmap info header.
425 if (!FlushContainedDecoder()) {
426 return Transition::TerminateFailure();
429 // If this is a metadata decode, FinishResource will any necessary checks.
430 if (mContainedDecoder->IsMetadataDecode()) {
431 return Transition::To(ICOState::FINISHED_RESOURCE, 0);
434 // Do we have an AND mask on this BMP? If so, we need to read it after we read
435 // the BMP data itself.
436 uint32_t bmpDataLength = bmpDecoder->GetCompressedImageSize() + 4 * numColors;
437 bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry->mBytesInRes;
438 ICOState afterBMPState =
439 hasANDMask ? ICOState::PREPARE_FOR_MASK : ICOState::FINISHED_RESOURCE;
441 // Read in the rest of the BMP unbuffered.
442 return Transition::ToUnbuffered(afterBMPState, ICOState::READ_RESOURCE,
443 bmpDataLength);
446 LexerTransition<ICOState> nsICODecoder::PrepareForMask() {
447 MOZ_ASSERT(mDirEntry);
448 MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
450 // We have received all of the data required by the BMP decoder so flushing
451 // here guarantees the decode has finished.
452 if (!FlushContainedDecoder()) {
453 return Transition::TerminateFailure();
456 MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
458 RefPtr<nsBMPDecoder> bmpDecoder =
459 static_cast<nsBMPDecoder*>(mContainedDecoder.get());
461 uint16_t numColors = GetNumColors();
462 MOZ_ASSERT(numColors != uint16_t(-1));
464 // Determine the length of the AND mask.
465 uint32_t bmpLengthWithHeader =
466 BITMAPINFOSIZE + bmpDecoder->GetCompressedImageSize() + 4 * numColors;
467 MOZ_ASSERT(bmpLengthWithHeader < mDirEntry->mBytesInRes);
468 uint32_t maskLength = mDirEntry->mBytesInRes - bmpLengthWithHeader;
470 // If the BMP provides its own transparency, we ignore the AND mask.
471 if (bmpDecoder->HasTransparency()) {
472 return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
473 ICOState::SKIP_MASK, maskLength);
476 // Compute the row size for the mask.
477 mMaskRowSize = ((mDirEntry->mSize.width + 31) / 32) * 4; // + 31 to round up
479 // If the expected size of the AND mask is larger than its actual size, then
480 // we must have a truncated (and therefore corrupt) AND mask.
481 uint32_t expectedLength = mMaskRowSize * mDirEntry->mSize.height;
482 if (maskLength < expectedLength) {
483 return Transition::TerminateFailure();
486 // If we're downscaling, the mask is the wrong size for the surface we've
487 // produced, so we need to downscale the mask into a temporary buffer and then
488 // combine the mask's alpha values with the color values from the image.
489 if (mDownscaler) {
490 MOZ_ASSERT(bmpDecoder->GetImageDataLength() ==
491 mDownscaler->TargetSize().width *
492 mDownscaler->TargetSize().height * sizeof(uint32_t));
493 mMaskBuffer = MakeUnique<uint8_t[]>(bmpDecoder->GetImageDataLength());
494 nsresult rv =
495 mDownscaler->BeginFrame(mDirEntry->mSize, Nothing(), mMaskBuffer.get(),
496 /* aHasAlpha = */ true,
497 /* aFlipVertically = */ true);
498 if (NS_FAILED(rv)) {
499 return Transition::TerminateFailure();
503 mCurrMaskLine = mDirEntry->mSize.height;
504 return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
507 LexerTransition<ICOState> nsICODecoder::ReadMaskRow(const char* aData) {
508 MOZ_ASSERT(mDirEntry);
510 mCurrMaskLine--;
512 uint8_t sawTransparency = 0;
514 // Get the mask row we're reading.
515 const uint8_t* mask = reinterpret_cast<const uint8_t*>(aData);
516 const uint8_t* maskRowEnd = mask + mMaskRowSize;
518 // Get the corresponding row of the mask buffer (if we're downscaling) or the
519 // decoded image data (if we're not).
520 uint32_t* decoded = nullptr;
521 if (mDownscaler) {
522 // Initialize the row to all white and fully opaque.
523 memset(mDownscaler->RowBuffer(), 0xFF,
524 mDirEntry->mSize.width * sizeof(uint32_t));
526 decoded = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
527 } else {
528 RefPtr<nsBMPDecoder> bmpDecoder =
529 static_cast<nsBMPDecoder*>(mContainedDecoder.get());
530 uint32_t* imageData = bmpDecoder->GetImageData();
531 if (!imageData) {
532 return Transition::TerminateFailure();
535 decoded = imageData + mCurrMaskLine * mDirEntry->mSize.width;
538 MOZ_ASSERT(decoded);
539 uint32_t* decodedRowEnd = decoded + mDirEntry->mSize.width;
541 // Iterate simultaneously through the AND mask and the image data.
542 while (mask < maskRowEnd) {
543 uint8_t idx = *mask++;
544 sawTransparency |= idx;
545 for (uint8_t bit = 0x80; bit && decoded < decodedRowEnd; bit >>= 1) {
546 // Clear pixel completely for transparency.
547 if (idx & bit) {
548 *decoded = 0;
550 decoded++;
554 if (mDownscaler) {
555 mDownscaler->CommitRow();
558 // If any bits are set in sawTransparency, then we know at least one pixel was
559 // transparent.
560 if (sawTransparency) {
561 mHasMaskAlpha = true;
564 if (mCurrMaskLine == 0) {
565 return Transition::To(ICOState::FINISH_MASK, 0);
568 return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
571 LexerTransition<ICOState> nsICODecoder::FinishMask() {
572 // If we're downscaling, we now have the appropriate alpha values in
573 // mMaskBuffer. We just need to transfer them to the image.
574 if (mDownscaler) {
575 // Retrieve the image data.
576 RefPtr<nsBMPDecoder> bmpDecoder =
577 static_cast<nsBMPDecoder*>(mContainedDecoder.get());
578 uint8_t* imageData = reinterpret_cast<uint8_t*>(bmpDecoder->GetImageData());
579 if (!imageData) {
580 return Transition::TerminateFailure();
583 // Iterate through the alpha values, copying from mask to image.
584 MOZ_ASSERT(mMaskBuffer);
585 MOZ_ASSERT(bmpDecoder->GetImageDataLength() > 0);
586 for (size_t i = 3; i < bmpDecoder->GetImageDataLength(); i += 4) {
587 imageData[i] = mMaskBuffer[i];
589 int32_t stride = mDownscaler->TargetSize().width * sizeof(uint32_t);
590 DebugOnly<bool> ret =
591 // We know the format is OS_RGBA because we always assume bmp's inside
592 // ico's are transparent.
593 PremultiplyData(imageData, stride, SurfaceFormat::OS_RGBA, imageData,
594 stride, SurfaceFormat::OS_RGBA,
595 mDownscaler->TargetSize());
596 MOZ_ASSERT(ret);
599 return Transition::To(ICOState::FINISHED_RESOURCE, 0);
602 LexerTransition<ICOState> nsICODecoder::FinishResource() {
603 MOZ_ASSERT(mDirEntry);
605 // We have received all of the data required by the PNG/BMP decoder so
606 // flushing here guarantees the decode has finished.
607 if (!FlushContainedDecoder()) {
608 return Transition::TerminateFailure();
611 MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
613 // If it is a metadata decode, all we were trying to get was the size
614 // information missing from the dir entry.
615 if (mContainedDecoder->IsMetadataDecode()) {
616 if (mContainedDecoder->HasSize()) {
617 mDirEntry->mSize = mContainedDecoder->Size();
619 return Transition::To(ICOState::ITERATE_UNSIZED_DIR_ENTRY, 0);
622 // Raymond Chen says that 32bpp only are valid PNG ICOs
623 // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
624 if (!mContainedDecoder->IsValidICOResource()) {
625 return Transition::TerminateFailure();
628 // This size from the resource should match that from the dir entry.
629 MOZ_ASSERT_IF(mContainedDecoder->HasSize(),
630 mContainedDecoder->Size() == mDirEntry->mSize);
632 return Transition::TerminateSuccess();
635 LexerResult nsICODecoder::DoDecode(SourceBufferIterator& aIterator,
636 IResumable* aOnResume) {
637 MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
639 return mLexer.Lex(
640 aIterator, aOnResume,
641 [=](ICOState aState, const char* aData, size_t aLength) {
642 switch (aState) {
643 case ICOState::HEADER:
644 return ReadHeader(aData);
645 case ICOState::DIR_ENTRY:
646 return ReadDirEntry(aData);
647 case ICOState::FINISHED_DIR_ENTRY:
648 return FinishDirEntry();
649 case ICOState::ITERATE_UNSIZED_DIR_ENTRY:
650 return IterateUnsizedDirEntry();
651 case ICOState::SKIP_TO_RESOURCE:
652 return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
653 case ICOState::FOUND_RESOURCE:
654 return Transition::To(ICOState::SNIFF_RESOURCE, BITMAPINFOSIZE);
655 case ICOState::SNIFF_RESOURCE:
656 return SniffResource(aData);
657 case ICOState::READ_RESOURCE:
658 return ReadResource();
659 case ICOState::PREPARE_FOR_MASK:
660 return PrepareForMask();
661 case ICOState::READ_MASK_ROW:
662 return ReadMaskRow(aData);
663 case ICOState::FINISH_MASK:
664 return FinishMask();
665 case ICOState::SKIP_MASK:
666 return Transition::ContinueUnbuffered(ICOState::SKIP_MASK);
667 case ICOState::FINISHED_RESOURCE:
668 return FinishResource();
669 default:
670 MOZ_CRASH("Unknown ICOState");
675 bool nsICODecoder::FlushContainedDecoder() {
676 MOZ_ASSERT(mContainedDecoder);
678 bool succeeded = true;
680 // If we run out of data, the ICO decoder will get resumed when there's more
681 // data available, as usual, so we don't need the contained decoder to get
682 // resumed too. To avoid that, we provide an IResumable which just does
683 // nothing. All the caller needs to do is flush when there is new data.
684 LexerResult result = mContainedDecoder->Decode();
685 if (result == LexerResult(TerminalState::FAILURE)) {
686 succeeded = false;
689 MOZ_ASSERT(result != LexerResult(Yield::OUTPUT_AVAILABLE),
690 "Unexpected yield");
692 // Make our state the same as the state of the contained decoder, and
693 // propagate errors.
694 mProgress |= mContainedDecoder->TakeProgress();
695 mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
696 if (mContainedDecoder->HasError()) {
697 succeeded = false;
700 return succeeded;
703 } // namespace image
704 } // namespace mozilla