зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 18614b05270d (bug 1315554)
This commit is contained in:
Родитель
d4bb320357
Коммит
8604a9cc71
|
@ -55,7 +55,9 @@ nsICODecoder::nsICODecoder(RasterImage* aImage)
|
||||||
: Decoder(aImage)
|
: Decoder(aImage)
|
||||||
, mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE),
|
, mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE),
|
||||||
Transition::TerminateSuccess())
|
Transition::TerminateSuccess())
|
||||||
, mDirEntry(nullptr)
|
, mBiggestResourceColorDepth(0)
|
||||||
|
, mBestResourceDelta(INT_MIN)
|
||||||
|
, mBestResourceColorDepth(0)
|
||||||
, mNumIcons(0)
|
, mNumIcons(0)
|
||||||
, mCurrIcon(0)
|
, mCurrIcon(0)
|
||||||
, mBPP(0)
|
, mBPP(0)
|
||||||
|
@ -149,176 +151,104 @@ nsICODecoder::ReadDirEntry(const char* aData)
|
||||||
{
|
{
|
||||||
mCurrIcon++;
|
mCurrIcon++;
|
||||||
|
|
||||||
// Ensure the resource has an offset past the ICO headers.
|
// Read the directory entry.
|
||||||
uint32_t offset = LittleEndian::readUint32(aData + 12);
|
IconDirEntry e;
|
||||||
if (offset >= FirstResourceOffset()) {
|
e.mWidth = aData[0];
|
||||||
// Read the directory entry.
|
e.mHeight = aData[1];
|
||||||
IconDirEntryEx e;
|
e.mColorCount = aData[2];
|
||||||
e.mWidth = aData[0];
|
e.mReserved = aData[3];
|
||||||
e.mHeight = aData[1];
|
e.mPlanes = LittleEndian::readUint16(aData + 4);
|
||||||
e.mColorCount = aData[2];
|
e.mBitCount = LittleEndian::readUint16(aData + 6);
|
||||||
e.mReserved = aData[3];
|
e.mBytesInRes = LittleEndian::readUint32(aData + 8);
|
||||||
e.mPlanes = LittleEndian::readUint16(aData + 4);
|
e.mImageOffset = LittleEndian::readUint32(aData + 12);
|
||||||
e.mBitCount = LittleEndian::readUint16(aData + 6);
|
|
||||||
e.mBytesInRes = LittleEndian::readUint32(aData + 8);
|
// If an explicit output size was specified, we'll try to select the resource
|
||||||
e.mImageOffset = offset;
|
// that matches it best below.
|
||||||
e.mSize = IntSize(e.mWidth, e.mHeight);
|
const Maybe<IntSize> desiredSize = ExplicitOutputSize();
|
||||||
if (e.mWidth == 0 || e.mHeight == 0) {
|
|
||||||
mUnsizedDirEntries.AppendElement(e);
|
// Determine if this is the biggest resource we've seen so far. We always use
|
||||||
} else {
|
// the biggest resource for the intrinsic size, and if we don't have a
|
||||||
mDirEntries.AppendElement(e);
|
// specific desired size, we select it as the best resource as well.
|
||||||
|
IntSize entrySize(GetRealWidth(e), GetRealHeight(e));
|
||||||
|
if (e.mBitCount >= mBiggestResourceColorDepth &&
|
||||||
|
entrySize.width * entrySize.height >=
|
||||||
|
mBiggestResourceSize.width * mBiggestResourceSize.height) {
|
||||||
|
mBiggestResourceSize = entrySize;
|
||||||
|
mBiggestResourceColorDepth = e.mBitCount;
|
||||||
|
mBiggestResourceHotSpot = IntSize(e.mXHotspot, e.mYHotspot);
|
||||||
|
|
||||||
|
if (!desiredSize) {
|
||||||
|
mDirEntry = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mImageMetadata.AddNativeSize(entrySize);
|
||||||
|
|
||||||
|
if (desiredSize) {
|
||||||
|
// Calculate the delta between this resource's size and the desired size, so
|
||||||
|
// we can see if it is better than our current-best option. In the case of
|
||||||
|
// several equally-good resources, we use the last one. "Better" in this
|
||||||
|
// case is determined by |delta|, a measure of the difference in size
|
||||||
|
// between the entry we've found and the desired size. We will choose the
|
||||||
|
// smallest resource that is greater than or equal to the desired size (i.e.
|
||||||
|
// we assume it's better to downscale a larger icon than to upscale a
|
||||||
|
// smaller one).
|
||||||
|
int32_t delta = std::min(entrySize.width - desiredSize->width,
|
||||||
|
entrySize.height - desiredSize->height);
|
||||||
|
if (e.mBitCount >= mBestResourceColorDepth &&
|
||||||
|
((mBestResourceDelta < 0 && delta >= mBestResourceDelta) ||
|
||||||
|
(delta >= 0 && delta <= mBestResourceDelta))) {
|
||||||
|
mBestResourceDelta = delta;
|
||||||
|
mBestResourceColorDepth = e.mBitCount;
|
||||||
|
mDirEntry = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCurrIcon == mNumIcons) {
|
if (mCurrIcon == mNumIcons) {
|
||||||
if (mUnsizedDirEntries.IsEmpty()) {
|
// Ensure the resource we selected has an offset past the ICO headers.
|
||||||
return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
|
if (mDirEntry.mImageOffset < FirstResourceOffset()) {
|
||||||
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
return Transition::To(ICOState::ITERATE_UNSIZED_DIR_ENTRY, 0);
|
|
||||||
|
// If this is a cursor, set the hotspot. We use the hotspot from the biggest
|
||||||
|
// resource since we also use that resource for the intrinsic size.
|
||||||
|
if (mIsCursor) {
|
||||||
|
mImageMetadata.SetHotspot(mBiggestResourceHotSpot.width,
|
||||||
|
mBiggestResourceHotSpot.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We always report the biggest resource's size as the intrinsic size; this
|
||||||
|
// is necessary for downscale-during-decode to work since we won't even
|
||||||
|
// attempt to *upscale* while decoding.
|
||||||
|
PostSize(mBiggestResourceSize.width, mBiggestResourceSize.height);
|
||||||
|
if (HasError()) {
|
||||||
|
return Transition::TerminateFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsMetadataDecode()) {
|
||||||
|
return Transition::TerminateSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the resource we selected matches the output size perfectly, we don't
|
||||||
|
// need to do any downscaling.
|
||||||
|
if (GetRealSize() == OutputSize()) {
|
||||||
|
MOZ_ASSERT_IF(desiredSize, GetRealSize() == *desiredSize);
|
||||||
|
MOZ_ASSERT_IF(!desiredSize, GetRealSize() == Size());
|
||||||
|
mDownscaler.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t offsetToResource = mDirEntry.mImageOffset - FirstResourceOffset();
|
||||||
|
return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
|
||||||
|
ICOState::SKIP_TO_RESOURCE,
|
||||||
|
offsetToResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
|
return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
LexerTransition<ICOState>
|
|
||||||
nsICODecoder::IterateUnsizedDirEntry()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!mUnsizedDirEntries.IsEmpty());
|
|
||||||
|
|
||||||
if (!mDirEntry) {
|
|
||||||
// The first time we are here, there is no entry selected. We must prepare a
|
|
||||||
// new iterator for the contained decoder to advance as it wills. Cloning at
|
|
||||||
// this point ensures it will begin at the end of the dir entries.
|
|
||||||
mReturnIterator.emplace(mLexer.Clone(*mIterator, SIZE_MAX));
|
|
||||||
} else {
|
|
||||||
// We have already selected an entry which means a metadata decoder has
|
|
||||||
// finished. Verify the size is valid and if so, add to the discovered
|
|
||||||
// resources.
|
|
||||||
if (mDirEntry->mSize.width > 0 && mDirEntry->mSize.height > 0) {
|
|
||||||
mDirEntries.AppendElement(*mDirEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the entry from the unsized list either way.
|
|
||||||
mDirEntry = nullptr;
|
|
||||||
mUnsizedDirEntries.RemoveElementAt(0);
|
|
||||||
|
|
||||||
// Our iterator is at an unknown point, so reset it to the point that we
|
|
||||||
// saved.
|
|
||||||
mIterator.reset();
|
|
||||||
mIterator.emplace(mLexer.Clone(*mReturnIterator, SIZE_MAX));
|
|
||||||
}
|
|
||||||
|
|
||||||
// There are no more unsized entries, so we can finally decide which entry to
|
|
||||||
// select for decoding.
|
|
||||||
if (mUnsizedDirEntries.IsEmpty()) {
|
|
||||||
mReturnIterator.reset();
|
|
||||||
return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the resource data to start metadata decoding.
|
|
||||||
mDirEntry = &mUnsizedDirEntries[0];
|
|
||||||
size_t offsetToResource = mDirEntry->mImageOffset - FirstResourceOffset();
|
|
||||||
return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
|
|
||||||
ICOState::SKIP_TO_RESOURCE,
|
|
||||||
offsetToResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
LexerTransition<ICOState>
|
|
||||||
nsICODecoder::FinishDirEntry()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!mDirEntry);
|
|
||||||
|
|
||||||
if (mDirEntries.IsEmpty()) {
|
|
||||||
return Transition::TerminateFailure();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an explicit output size was specified, we'll try to select the resource
|
|
||||||
// that matches it best below.
|
|
||||||
const Maybe<IntSize> desiredSize = ExplicitOutputSize();
|
|
||||||
|
|
||||||
// Determine the biggest resource. We always use the biggest resource for the
|
|
||||||
// intrinsic size, and if we don't have a specific desired size, we select it
|
|
||||||
// as the best resource as well.
|
|
||||||
int32_t bestDelta = INT32_MIN;
|
|
||||||
IconDirEntryEx* biggestEntry = nullptr;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < mDirEntries.Length(); ++i) {
|
|
||||||
IconDirEntryEx& e = mDirEntries[i];
|
|
||||||
mImageMetadata.AddNativeSize(e.mSize);
|
|
||||||
|
|
||||||
if (!biggestEntry ||
|
|
||||||
(e.mBitCount >= biggestEntry->mBitCount &&
|
|
||||||
e.mSize.width * e.mSize.height >=
|
|
||||||
biggestEntry->mSize.width * biggestEntry->mSize.height)) {
|
|
||||||
biggestEntry = &e;
|
|
||||||
|
|
||||||
if (!desiredSize) {
|
|
||||||
mDirEntry = &e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (desiredSize) {
|
|
||||||
// Calculate the delta between this resource's size and the desired size, so
|
|
||||||
// we can see if it is better than our current-best option. In the case of
|
|
||||||
// several equally-good resources, we use the last one. "Better" in this
|
|
||||||
// case is determined by |delta|, a measure of the difference in size
|
|
||||||
// between the entry we've found and the desired size. We will choose the
|
|
||||||
// smallest resource that is greater than or equal to the desired size (i.e.
|
|
||||||
// we assume it's better to downscale a larger icon than to upscale a
|
|
||||||
// smaller one).
|
|
||||||
int32_t delta = std::min(e.mSize.width - desiredSize->width,
|
|
||||||
e.mSize.height - desiredSize->height);
|
|
||||||
if (!mDirEntry ||
|
|
||||||
(e.mBitCount >= mDirEntry->mBitCount &&
|
|
||||||
((bestDelta < 0 && delta >= bestDelta) ||
|
|
||||||
(delta >= 0 && delta <= bestDelta)))) {
|
|
||||||
mDirEntry = &e;
|
|
||||||
bestDelta = delta;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT(mDirEntry);
|
|
||||||
MOZ_ASSERT(biggestEntry);
|
|
||||||
|
|
||||||
// If this is a cursor, set the hotspot. We use the hotspot from the biggest
|
|
||||||
// resource since we also use that resource for the intrinsic size.
|
|
||||||
if (mIsCursor) {
|
|
||||||
mImageMetadata.SetHotspot(biggestEntry->mXHotspot,
|
|
||||||
biggestEntry->mYHotspot);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We always report the biggest resource's size as the intrinsic size; this
|
|
||||||
// is necessary for downscale-during-decode to work since we won't even
|
|
||||||
// attempt to *upscale* while decoding.
|
|
||||||
PostSize(biggestEntry->mSize.width, biggestEntry->mSize.height);
|
|
||||||
if (HasError()) {
|
|
||||||
return Transition::TerminateFailure();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsMetadataDecode()) {
|
|
||||||
return Transition::TerminateSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the resource we selected matches the output size perfectly, we don't
|
|
||||||
// need to do any downscaling.
|
|
||||||
if (mDirEntry->mSize == OutputSize()) {
|
|
||||||
MOZ_ASSERT_IF(desiredSize, mDirEntry->mSize == *desiredSize);
|
|
||||||
MOZ_ASSERT_IF(!desiredSize, mDirEntry->mSize == Size());
|
|
||||||
mDownscaler.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t offsetToResource = mDirEntry->mImageOffset - FirstResourceOffset();
|
|
||||||
return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
|
|
||||||
ICOState::SKIP_TO_RESOURCE,
|
|
||||||
offsetToResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
LexerTransition<ICOState>
|
LexerTransition<ICOState>
|
||||||
nsICODecoder::SniffResource(const char* aData)
|
nsICODecoder::SniffResource(const char* aData)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mDirEntry);
|
|
||||||
|
|
||||||
// We have BITMAPINFOSIZE bytes buffered at this point. We know an embedded
|
// We have BITMAPINFOSIZE bytes buffered at this point. We know an embedded
|
||||||
// BMP will have at least that many bytes by definition. We can also infer
|
// BMP will have at least that many bytes by definition. We can also infer
|
||||||
// that any valid embedded PNG will contain that many bytes as well because:
|
// that any valid embedded PNG will contain that many bytes as well because:
|
||||||
|
@ -333,28 +263,25 @@ nsICODecoder::SniffResource(const char* aData)
|
||||||
bool isPNG = !memcmp(aData, nsPNGDecoder::pngSignatureBytes,
|
bool isPNG = !memcmp(aData, nsPNGDecoder::pngSignatureBytes,
|
||||||
PNGSIGNATURESIZE);
|
PNGSIGNATURESIZE);
|
||||||
if (isPNG) {
|
if (isPNG) {
|
||||||
if (mDirEntry->mBytesInRes <= BITMAPINFOSIZE) {
|
if (mDirEntry.mBytesInRes <= BITMAPINFOSIZE) {
|
||||||
return Transition::TerminateFailure();
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare a new iterator for the contained decoder to advance as it wills.
|
// Prepare a new iterator for the contained decoder to advance as it wills.
|
||||||
// Cloning at the point ensures it will begin at the resource offset.
|
// Cloning at the point ensures it will begin at the resource offset.
|
||||||
SourceBufferIterator containedIterator
|
SourceBufferIterator containedIterator
|
||||||
= mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
|
= mLexer.Clone(*mIterator, mDirEntry.mBytesInRes);
|
||||||
|
|
||||||
// Create a PNG decoder which will do the rest of the work for us.
|
// Create a PNG decoder which will do the rest of the work for us.
|
||||||
bool metadataDecode = mReturnIterator.isSome();
|
|
||||||
Maybe<IntSize> expectedSize = metadataDecode ? Nothing()
|
|
||||||
: Some(mDirEntry->mSize);
|
|
||||||
mContainedDecoder =
|
mContainedDecoder =
|
||||||
DecoderFactory::CreateDecoderForICOResource(DecoderType::PNG,
|
DecoderFactory::CreateDecoderForICOResource(DecoderType::PNG,
|
||||||
Move(containedIterator),
|
Move(containedIterator),
|
||||||
WrapNotNull(this),
|
WrapNotNull(this),
|
||||||
metadataDecode,
|
false,
|
||||||
expectedSize);
|
Some(GetRealSize()));
|
||||||
|
|
||||||
// Read in the rest of the PNG unbuffered.
|
// Read in the rest of the PNG unbuffered.
|
||||||
size_t toRead = mDirEntry->mBytesInRes - BITMAPINFOSIZE;
|
size_t toRead = mDirEntry.mBytesInRes - BITMAPINFOSIZE;
|
||||||
return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
|
return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
|
||||||
ICOState::READ_RESOURCE,
|
ICOState::READ_RESOURCE,
|
||||||
toRead);
|
toRead);
|
||||||
|
@ -383,8 +310,6 @@ nsICODecoder::ReadResource()
|
||||||
LexerTransition<ICOState>
|
LexerTransition<ICOState>
|
||||||
nsICODecoder::ReadBIH(const char* aData)
|
nsICODecoder::ReadBIH(const char* aData)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mDirEntry);
|
|
||||||
|
|
||||||
// Extract the BPP from the BIH header; it should be trusted over the one
|
// Extract the BPP from the BIH header; it should be trusted over the one
|
||||||
// we have from the ICO header which is usually set to 0.
|
// we have from the ICO header which is usually set to 0.
|
||||||
mBPP = LittleEndian::readUint16(aData + 14);
|
mBPP = LittleEndian::readUint16(aData + 14);
|
||||||
|
@ -407,19 +332,16 @@ nsICODecoder::ReadBIH(const char* aData)
|
||||||
// Prepare a new iterator for the contained decoder to advance as it wills.
|
// Prepare a new iterator for the contained decoder to advance as it wills.
|
||||||
// Cloning at the point ensures it will begin at the resource offset.
|
// Cloning at the point ensures it will begin at the resource offset.
|
||||||
SourceBufferIterator containedIterator
|
SourceBufferIterator containedIterator
|
||||||
= mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
|
= mLexer.Clone(*mIterator, mDirEntry.mBytesInRes);
|
||||||
|
|
||||||
// Create a BMP decoder which will do most of the work for us; the exception
|
// Create a BMP decoder which will do most of the work for us; the exception
|
||||||
// is the AND mask, which isn't present in standalone BMPs.
|
// is the AND mask, which isn't present in standalone BMPs.
|
||||||
bool metadataDecode = mReturnIterator.isSome();
|
|
||||||
Maybe<IntSize> expectedSize = metadataDecode ? Nothing()
|
|
||||||
: Some(mDirEntry->mSize);
|
|
||||||
mContainedDecoder =
|
mContainedDecoder =
|
||||||
DecoderFactory::CreateDecoderForICOResource(DecoderType::BMP,
|
DecoderFactory::CreateDecoderForICOResource(DecoderType::BMP,
|
||||||
Move(containedIterator),
|
Move(containedIterator),
|
||||||
WrapNotNull(this),
|
WrapNotNull(this),
|
||||||
metadataDecode,
|
false,
|
||||||
expectedSize,
|
Some(GetRealSize()),
|
||||||
Some(dataOffset));
|
Some(dataOffset));
|
||||||
|
|
||||||
RefPtr<nsBMPDecoder> bmpDecoder =
|
RefPtr<nsBMPDecoder> bmpDecoder =
|
||||||
|
@ -430,15 +352,10 @@ nsICODecoder::ReadBIH(const char* aData)
|
||||||
return Transition::TerminateFailure();
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a metadata decode, FinishResource will any necessary checks.
|
|
||||||
if (mContainedDecoder->IsMetadataDecode()) {
|
|
||||||
return Transition::To(ICOState::FINISHED_RESOURCE, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do we have an AND mask on this BMP? If so, we need to read it after we read
|
// Do we have an AND mask on this BMP? If so, we need to read it after we read
|
||||||
// the BMP data itself.
|
// the BMP data itself.
|
||||||
uint32_t bmpDataLength = bmpDecoder->GetCompressedImageSize() + 4 * numColors;
|
uint32_t bmpDataLength = bmpDecoder->GetCompressedImageSize() + 4 * numColors;
|
||||||
bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry->mBytesInRes;
|
bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry.mBytesInRes;
|
||||||
ICOState afterBMPState = hasANDMask ? ICOState::PREPARE_FOR_MASK
|
ICOState afterBMPState = hasANDMask ? ICOState::PREPARE_FOR_MASK
|
||||||
: ICOState::FINISHED_RESOURCE;
|
: ICOState::FINISHED_RESOURCE;
|
||||||
|
|
||||||
|
@ -451,9 +368,6 @@ nsICODecoder::ReadBIH(const char* aData)
|
||||||
LexerTransition<ICOState>
|
LexerTransition<ICOState>
|
||||||
nsICODecoder::PrepareForMask()
|
nsICODecoder::PrepareForMask()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mDirEntry);
|
|
||||||
MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
|
|
||||||
|
|
||||||
// We have received all of the data required by the BMP decoder so flushing
|
// We have received all of the data required by the BMP decoder so flushing
|
||||||
// here guarantees the decode has finished.
|
// here guarantees the decode has finished.
|
||||||
if (!FlushContainedDecoder()) {
|
if (!FlushContainedDecoder()) {
|
||||||
|
@ -471,22 +385,24 @@ nsICODecoder::PrepareForMask()
|
||||||
// Determine the length of the AND mask.
|
// Determine the length of the AND mask.
|
||||||
uint32_t bmpLengthWithHeader =
|
uint32_t bmpLengthWithHeader =
|
||||||
BITMAPINFOSIZE + bmpDecoder->GetCompressedImageSize() + 4 * numColors;
|
BITMAPINFOSIZE + bmpDecoder->GetCompressedImageSize() + 4 * numColors;
|
||||||
MOZ_ASSERT(bmpLengthWithHeader < mDirEntry->mBytesInRes);
|
MOZ_ASSERT(bmpLengthWithHeader < mDirEntry.mBytesInRes);
|
||||||
uint32_t maskLength = mDirEntry->mBytesInRes - bmpLengthWithHeader;
|
uint32_t maskLength = mDirEntry.mBytesInRes - bmpLengthWithHeader;
|
||||||
|
|
||||||
// If the BMP provides its own transparency, we ignore the AND mask.
|
// If the BMP provides its own transparency, we ignore the AND mask. We can
|
||||||
if (bmpDecoder->HasTransparency()) {
|
// also obviously ignore it if the image has zero width or zero height.
|
||||||
|
if (bmpDecoder->HasTransparency() ||
|
||||||
|
GetRealWidth() == 0 || GetRealHeight() == 0) {
|
||||||
return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
|
return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
|
||||||
ICOState::SKIP_MASK,
|
ICOState::SKIP_MASK,
|
||||||
maskLength);
|
maskLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the row size for the mask.
|
// Compute the row size for the mask.
|
||||||
mMaskRowSize = ((mDirEntry->mSize.width + 31) / 32) * 4; // + 31 to round up
|
mMaskRowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
|
||||||
|
|
||||||
// If the expected size of the AND mask is larger than its actual size, then
|
// If the expected size of the AND mask is larger than its actual size, then
|
||||||
// we must have a truncated (and therefore corrupt) AND mask.
|
// we must have a truncated (and therefore corrupt) AND mask.
|
||||||
uint32_t expectedLength = mMaskRowSize * mDirEntry->mSize.height;
|
uint32_t expectedLength = mMaskRowSize * GetRealHeight();
|
||||||
if (maskLength < expectedLength) {
|
if (maskLength < expectedLength) {
|
||||||
return Transition::TerminateFailure();
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
@ -500,7 +416,7 @@ nsICODecoder::PrepareForMask()
|
||||||
mDownscaler->TargetSize().height *
|
mDownscaler->TargetSize().height *
|
||||||
sizeof(uint32_t));
|
sizeof(uint32_t));
|
||||||
mMaskBuffer = MakeUnique<uint8_t[]>(bmpDecoder->GetImageDataLength());
|
mMaskBuffer = MakeUnique<uint8_t[]>(bmpDecoder->GetImageDataLength());
|
||||||
nsresult rv = mDownscaler->BeginFrame(mDirEntry->mSize, Nothing(),
|
nsresult rv = mDownscaler->BeginFrame(GetRealSize(), Nothing(),
|
||||||
mMaskBuffer.get(),
|
mMaskBuffer.get(),
|
||||||
/* aHasAlpha = */ true,
|
/* aHasAlpha = */ true,
|
||||||
/* aFlipVertically = */ true);
|
/* aFlipVertically = */ true);
|
||||||
|
@ -509,7 +425,7 @@ nsICODecoder::PrepareForMask()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mCurrMaskLine = mDirEntry->mSize.height;
|
mCurrMaskLine = GetRealHeight();
|
||||||
return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
|
return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,8 +433,6 @@ nsICODecoder::PrepareForMask()
|
||||||
LexerTransition<ICOState>
|
LexerTransition<ICOState>
|
||||||
nsICODecoder::ReadMaskRow(const char* aData)
|
nsICODecoder::ReadMaskRow(const char* aData)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mDirEntry);
|
|
||||||
|
|
||||||
mCurrMaskLine--;
|
mCurrMaskLine--;
|
||||||
|
|
||||||
uint8_t sawTransparency = 0;
|
uint8_t sawTransparency = 0;
|
||||||
|
@ -532,7 +446,7 @@ nsICODecoder::ReadMaskRow(const char* aData)
|
||||||
uint32_t* decoded = nullptr;
|
uint32_t* decoded = nullptr;
|
||||||
if (mDownscaler) {
|
if (mDownscaler) {
|
||||||
// Initialize the row to all white and fully opaque.
|
// Initialize the row to all white and fully opaque.
|
||||||
memset(mDownscaler->RowBuffer(), 0xFF, mDirEntry->mSize.width * sizeof(uint32_t));
|
memset(mDownscaler->RowBuffer(), 0xFF, GetRealWidth() * sizeof(uint32_t));
|
||||||
|
|
||||||
decoded = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
|
decoded = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
|
||||||
} else {
|
} else {
|
||||||
|
@ -543,11 +457,11 @@ nsICODecoder::ReadMaskRow(const char* aData)
|
||||||
return Transition::TerminateFailure();
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
decoded = imageData + mCurrMaskLine * mDirEntry->mSize.width;
|
decoded = imageData + mCurrMaskLine * GetRealWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(decoded);
|
MOZ_ASSERT(decoded);
|
||||||
uint32_t* decodedRowEnd = decoded + mDirEntry->mSize.width;
|
uint32_t* decodedRowEnd = decoded + GetRealWidth();
|
||||||
|
|
||||||
// Iterate simultaneously through the AND mask and the image data.
|
// Iterate simultaneously through the AND mask and the image data.
|
||||||
while (mask < maskRowEnd) {
|
while (mask < maskRowEnd) {
|
||||||
|
@ -607,8 +521,6 @@ nsICODecoder::FinishMask()
|
||||||
LexerTransition<ICOState>
|
LexerTransition<ICOState>
|
||||||
nsICODecoder::FinishResource()
|
nsICODecoder::FinishResource()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mDirEntry);
|
|
||||||
|
|
||||||
// We have received all of the data required by the PNG/BMP decoder so
|
// We have received all of the data required by the PNG/BMP decoder so
|
||||||
// flushing here guarantees the decode has finished.
|
// flushing here guarantees the decode has finished.
|
||||||
if (!FlushContainedDecoder()) {
|
if (!FlushContainedDecoder()) {
|
||||||
|
@ -617,13 +529,11 @@ nsICODecoder::FinishResource()
|
||||||
|
|
||||||
MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
|
MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
|
||||||
|
|
||||||
// If it is a metadata decode, all we were trying to get was the size
|
// Make sure the actual size of the resource matches the size in the directory
|
||||||
// information missing from the dir entry.
|
// entry. If not, we consider the image corrupt.
|
||||||
if (mContainedDecoder->IsMetadataDecode()) {
|
if (mContainedDecoder->HasSize() &&
|
||||||
if (mContainedDecoder->HasSize()) {
|
mContainedDecoder->Size() != GetRealSize()) {
|
||||||
mDirEntry->mSize = mContainedDecoder->Size();
|
return Transition::TerminateFailure();
|
||||||
}
|
|
||||||
return Transition::To(ICOState::ITERATE_UNSIZED_DIR_ENTRY, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raymond Chen says that 32bpp only are valid PNG ICOs
|
// Raymond Chen says that 32bpp only are valid PNG ICOs
|
||||||
|
@ -632,10 +542,6 @@ nsICODecoder::FinishResource()
|
||||||
return Transition::TerminateFailure();
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This size from the resource should match that from the dir entry.
|
|
||||||
MOZ_ASSERT_IF(mContainedDecoder->HasSize(),
|
|
||||||
mContainedDecoder->Size() == mDirEntry->mSize);
|
|
||||||
|
|
||||||
// Finalize the frame which we deferred to ensure we could modify the final
|
// Finalize the frame which we deferred to ensure we could modify the final
|
||||||
// result (e.g. to apply the BMP mask).
|
// result (e.g. to apply the BMP mask).
|
||||||
MOZ_ASSERT(!mContainedDecoder->GetFinalizeFrames());
|
MOZ_ASSERT(!mContainedDecoder->GetFinalizeFrames());
|
||||||
|
@ -658,10 +564,6 @@ nsICODecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
|
||||||
return ReadHeader(aData);
|
return ReadHeader(aData);
|
||||||
case ICOState::DIR_ENTRY:
|
case ICOState::DIR_ENTRY:
|
||||||
return ReadDirEntry(aData);
|
return ReadDirEntry(aData);
|
||||||
case ICOState::FINISHED_DIR_ENTRY:
|
|
||||||
return FinishDirEntry();
|
|
||||||
case ICOState::ITERATE_UNSIZED_DIR_ENTRY:
|
|
||||||
return IterateUnsizedDirEntry();
|
|
||||||
case ICOState::SKIP_TO_RESOURCE:
|
case ICOState::SKIP_TO_RESOURCE:
|
||||||
return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
|
return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
|
||||||
case ICOState::FOUND_RESOURCE:
|
case ICOState::FOUND_RESOURCE:
|
||||||
|
|
|
@ -24,8 +24,6 @@ enum class ICOState
|
||||||
{
|
{
|
||||||
HEADER,
|
HEADER,
|
||||||
DIR_ENTRY,
|
DIR_ENTRY,
|
||||||
FINISHED_DIR_ENTRY,
|
|
||||||
ITERATE_UNSIZED_DIR_ENTRY,
|
|
||||||
SKIP_TO_RESOURCE,
|
SKIP_TO_RESOURCE,
|
||||||
FOUND_RESOURCE,
|
FOUND_RESOURCE,
|
||||||
SNIFF_RESOURCE,
|
SNIFF_RESOURCE,
|
||||||
|
@ -42,6 +40,30 @@ class nsICODecoder : public Decoder
|
||||||
public:
|
public:
|
||||||
virtual ~nsICODecoder() { }
|
virtual ~nsICODecoder() { }
|
||||||
|
|
||||||
|
/// @return the width of the icon directory entry @aEntry.
|
||||||
|
static uint32_t GetRealWidth(const IconDirEntry& aEntry)
|
||||||
|
{
|
||||||
|
return aEntry.mWidth == 0 ? 256 : aEntry.mWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return the width of the selected directory entry (mDirEntry).
|
||||||
|
uint32_t GetRealWidth() const { return GetRealWidth(mDirEntry); }
|
||||||
|
|
||||||
|
/// @return the height of the icon directory entry @aEntry.
|
||||||
|
static uint32_t GetRealHeight(const IconDirEntry& aEntry)
|
||||||
|
{
|
||||||
|
return aEntry.mHeight == 0 ? 256 : aEntry.mHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return the height of the selected directory entry (mDirEntry).
|
||||||
|
uint32_t GetRealHeight() const { return GetRealHeight(mDirEntry); }
|
||||||
|
|
||||||
|
/// @return the size of the selected directory entry (mDirEntry).
|
||||||
|
gfx::IntSize GetRealSize() const
|
||||||
|
{
|
||||||
|
return gfx::IntSize(GetRealWidth(), GetRealHeight());
|
||||||
|
}
|
||||||
|
|
||||||
/// @return The offset from the beginning of the ICO to the first resource.
|
/// @return The offset from the beginning of the ICO to the first resource.
|
||||||
size_t FirstResourceOffset() const;
|
size_t FirstResourceOffset() const;
|
||||||
|
|
||||||
|
@ -68,8 +90,6 @@ private:
|
||||||
|
|
||||||
LexerTransition<ICOState> ReadHeader(const char* aData);
|
LexerTransition<ICOState> ReadHeader(const char* aData);
|
||||||
LexerTransition<ICOState> ReadDirEntry(const char* aData);
|
LexerTransition<ICOState> ReadDirEntry(const char* aData);
|
||||||
LexerTransition<ICOState> IterateUnsizedDirEntry();
|
|
||||||
LexerTransition<ICOState> FinishDirEntry();
|
|
||||||
LexerTransition<ICOState> SniffResource(const char* aData);
|
LexerTransition<ICOState> SniffResource(const char* aData);
|
||||||
LexerTransition<ICOState> ReadResource();
|
LexerTransition<ICOState> ReadResource();
|
||||||
LexerTransition<ICOState> ReadBIH(const char* aData);
|
LexerTransition<ICOState> ReadBIH(const char* aData);
|
||||||
|
@ -78,20 +98,18 @@ private:
|
||||||
LexerTransition<ICOState> FinishMask();
|
LexerTransition<ICOState> FinishMask();
|
||||||
LexerTransition<ICOState> FinishResource();
|
LexerTransition<ICOState> FinishResource();
|
||||||
|
|
||||||
struct IconDirEntryEx : public IconDirEntry {
|
|
||||||
gfx::IntSize mSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
StreamingLexer<ICOState, 32> mLexer; // The lexer.
|
StreamingLexer<ICOState, 32> mLexer; // The lexer.
|
||||||
RefPtr<Decoder> mContainedDecoder; // Either a BMP or PNG decoder.
|
RefPtr<Decoder> mContainedDecoder; // Either a BMP or PNG decoder.
|
||||||
Maybe<SourceBufferIterator> mReturnIterator; // Iterator to save return point.
|
UniquePtr<uint8_t[]> mMaskBuffer; // A temporary buffer for the alpha mask.
|
||||||
UniquePtr<uint8_t[]> mMaskBuffer; // A temporary buffer for the alpha mask.
|
IconDirEntry mDirEntry; // The dir entry for the selected resource.
|
||||||
nsTArray<IconDirEntryEx> mDirEntries; // Valid dir entries with a size.
|
gfx::IntSize mBiggestResourceSize; // Used to select the intrinsic size.
|
||||||
nsTArray<IconDirEntryEx> mUnsizedDirEntries; // Dir entries without a size.
|
gfx::IntSize mBiggestResourceHotSpot; // Used to select the intrinsic size.
|
||||||
IconDirEntryEx* mDirEntry; // The dir entry for the selected resource.
|
uint16_t mBiggestResourceColorDepth; // Used to select the intrinsic size.
|
||||||
uint16_t mNumIcons; // Stores the number of icons in the ICO file.
|
int32_t mBestResourceDelta; // Used to select the best resource.
|
||||||
uint16_t mCurrIcon; // Stores the current dir entry index we are processing.
|
uint16_t mBestResourceColorDepth; // Used to select the best resource.
|
||||||
uint16_t mBPP; // The BPP of the resource we're decoding.
|
uint16_t mNumIcons; // Stores the number of icons in the ICO file.
|
||||||
|
uint16_t mCurrIcon; // Stores the current dir entry index we are processing.
|
||||||
|
uint16_t mBPP; // The BPP of the resource we're decoding.
|
||||||
uint32_t mMaskRowSize; // The size in bytes of each row in the BMP alpha mask.
|
uint32_t mMaskRowSize; // The size in bytes of each row in the BMP alpha mask.
|
||||||
uint32_t mCurrMaskLine; // The line of the BMP alpha mask we're processing.
|
uint32_t mCurrMaskLine; // The line of the BMP alpha mask we're processing.
|
||||||
bool mIsCursor; // Is this ICO a cursor?
|
bool mIsCursor; // Is this ICO a cursor?
|
||||||
|
|
Загрузка…
Ссылка в новой задаче