mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1204393 (part 1) - Use StreamingLexer in the ICON decoder. r=seth.
* * * Bug 1204393 (part 1b) - Address review comments.
This commit is contained in:
parent
2ca445269a
commit
ba0768489b
@ -1,6 +1,6 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
@ -19,13 +19,15 @@ using std::min;
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
static const uint32_t ICON_HEADER_SIZE = 2;
|
||||
|
||||
nsIconDecoder::nsIconDecoder(RasterImage* aImage)
|
||||
: Decoder(aImage)
|
||||
, mExpectedDataLength(0)
|
||||
, mPixBytesRead(0)
|
||||
, mState(iconStateStart)
|
||||
, mWidth(-1)
|
||||
, mHeight(-1)
|
||||
, mLexer(Transition::To(State::HEADER, ICON_HEADER_SIZE))
|
||||
, mWidth() // set by ReadHeader()
|
||||
, mHeight() // set by ReadHeader()
|
||||
, mBytesPerRow() // set by ReadHeader()
|
||||
, mCurrentRow(0)
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
@ -37,149 +39,110 @@ void
|
||||
nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
||||
{
|
||||
MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
|
||||
MOZ_ASSERT(aBuffer);
|
||||
MOZ_ASSERT(aCount > 0);
|
||||
|
||||
// Loop until the input data is gone
|
||||
while (aCount > 0) {
|
||||
switch (mState) {
|
||||
case iconStateStart:
|
||||
|
||||
// Grab the width
|
||||
mWidth = (uint8_t)*aBuffer;
|
||||
|
||||
// Book Keeping
|
||||
aBuffer++;
|
||||
aCount--;
|
||||
mState = iconStateHaveHeight;
|
||||
break;
|
||||
|
||||
case iconStateHaveHeight:
|
||||
|
||||
// Grab the Height
|
||||
mHeight = (uint8_t)*aBuffer;
|
||||
|
||||
// Post our size to the superclass
|
||||
PostSize(mWidth, mHeight);
|
||||
|
||||
PostHasTransparency();
|
||||
|
||||
if (HasError()) {
|
||||
// Setting the size led to an error.
|
||||
mState = iconStateFinished;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're doing a metadata decode, we're done.
|
||||
if (IsMetadataDecode()) {
|
||||
mState = iconStateFinished;
|
||||
break;
|
||||
}
|
||||
|
||||
// The input is 32bpp, so we expect 4 bytes of data per pixel.
|
||||
mExpectedDataLength = mWidth * mHeight * 4;
|
||||
|
||||
{
|
||||
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
|
||||
IntSize targetSize = mDownscaler ? mDownscaler->TargetSize()
|
||||
: GetSize();
|
||||
nsresult rv = AllocateFrame(0, targetSize,
|
||||
IntRect(IntPoint(), targetSize),
|
||||
gfx::SurfaceFormat::B8G8R8A8);
|
||||
if (NS_FAILED(rv)) {
|
||||
mState = iconStateFinished;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mImageData, "Should have a buffer now");
|
||||
|
||||
if (mDownscaler) {
|
||||
nsresult rv = mDownscaler->BeginFrame(GetSize(), Nothing(),
|
||||
mImageData,
|
||||
/* aHasAlpha = */ true);
|
||||
if (NS_FAILED(rv)) {
|
||||
mState = iconStateFinished;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Book Keeping
|
||||
aBuffer++;
|
||||
aCount--;
|
||||
mState = iconStateReadPixels;
|
||||
break;
|
||||
|
||||
case iconStateReadPixels: {
|
||||
|
||||
// How many bytes are we reading?
|
||||
uint32_t bytesToRead = min(aCount, mExpectedDataLength - mPixBytesRead);
|
||||
|
||||
if (mDownscaler) {
|
||||
uint8_t* row = mDownscaler->RowBuffer();
|
||||
const uint32_t bytesPerRow = mWidth * 4;
|
||||
const uint32_t rowOffset = mPixBytesRead % bytesPerRow;
|
||||
|
||||
// Update global state; we're about to read |bytesToRead| bytes.
|
||||
aCount -= bytesToRead;
|
||||
mPixBytesRead += bytesToRead;
|
||||
|
||||
if (rowOffset > 0) {
|
||||
// Finish the current row.
|
||||
const uint32_t remaining = bytesPerRow - rowOffset;
|
||||
memcpy(row + rowOffset, aBuffer, remaining);
|
||||
aBuffer += remaining;
|
||||
bytesToRead -= remaining;
|
||||
mDownscaler->CommitRow();
|
||||
}
|
||||
|
||||
// Copy the bytes a row at a time.
|
||||
while (bytesToRead > bytesPerRow) {
|
||||
memcpy(row, aBuffer, bytesPerRow);
|
||||
aBuffer += bytesPerRow;
|
||||
bytesToRead -= bytesPerRow;
|
||||
mDownscaler->CommitRow();
|
||||
}
|
||||
|
||||
// Copy any leftover bytes. (Leaving the current row incomplete.)
|
||||
if (bytesToRead > 0) {
|
||||
memcpy(row, aBuffer, bytesToRead);
|
||||
aBuffer += bytesPerRow;
|
||||
bytesToRead -= bytesPerRow;
|
||||
}
|
||||
|
||||
if (mDownscaler->HasInvalidation()) {
|
||||
DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
|
||||
PostInvalidation(invalidRect.mOriginalSizeRect,
|
||||
Some(invalidRect.mTargetSizeRect));
|
||||
}
|
||||
} else {
|
||||
// Copy all the bytes at once.
|
||||
memcpy(mImageData + mPixBytesRead, aBuffer, bytesToRead);
|
||||
aBuffer += bytesToRead;
|
||||
aCount -= bytesToRead;
|
||||
mPixBytesRead += bytesToRead;
|
||||
|
||||
// Invalidate. Performance isn't critical here, so our update
|
||||
// rectangle is always the full icon.
|
||||
PostInvalidation(IntRect(0, 0, mWidth, mHeight));
|
||||
}
|
||||
|
||||
// If we've got all the pixel bytes, we're finished
|
||||
if (mPixBytesRead == mExpectedDataLength) {
|
||||
PostFrameStop();
|
||||
PostDecodeDone();
|
||||
mState = iconStateFinished;
|
||||
}
|
||||
break;
|
||||
Maybe<State> terminalState =
|
||||
mLexer.Lex(aBuffer, aCount, [=](State aState,
|
||||
const char* aData, size_t aLength) {
|
||||
switch (aState) {
|
||||
case State::HEADER:
|
||||
return ReadHeader(aData);
|
||||
case State::ROW_OF_PIXELS:
|
||||
return ReadRowOfPixels(aData, aLength);
|
||||
case State::FINISH:
|
||||
return Finish();
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown State");
|
||||
return Transition::Terminate(State::FAILURE);
|
||||
}
|
||||
});
|
||||
|
||||
case iconStateFinished:
|
||||
if (!terminalState) {
|
||||
return; // Need more data.
|
||||
}
|
||||
|
||||
// Consume all excess data silently
|
||||
aCount = 0;
|
||||
if (*terminalState == State::FAILURE) {
|
||||
PostDataError();
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
MOZ_ASSERT(*terminalState == State::SUCCESS);
|
||||
}
|
||||
|
||||
LexerTransition<nsIconDecoder::State>
|
||||
nsIconDecoder::ReadHeader(const char* aData)
|
||||
{
|
||||
// Grab the width and height.
|
||||
mWidth = uint8_t(aData[0]);
|
||||
mHeight = uint8_t(aData[1]);
|
||||
|
||||
// The input is 32bpp, so we expect 4 bytes of data per pixel.
|
||||
mBytesPerRow = mWidth * 4;
|
||||
|
||||
// Post our size to the superclass.
|
||||
PostSize(mWidth, mHeight);
|
||||
|
||||
// Icons have alpha.
|
||||
PostHasTransparency();
|
||||
|
||||
// If we're doing a metadata decode, we're done.
|
||||
if (IsMetadataDecode()) {
|
||||
return Transition::Terminate(State::SUCCESS);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
|
||||
IntSize targetSize = mDownscaler ? mDownscaler->TargetSize() : GetSize();
|
||||
nsresult rv = AllocateFrame(0, targetSize,
|
||||
IntRect(IntPoint(), targetSize),
|
||||
gfx::SurfaceFormat::B8G8R8A8);
|
||||
if (NS_FAILED(rv)) {
|
||||
return Transition::Terminate(State::FAILURE);
|
||||
}
|
||||
MOZ_ASSERT(mImageData, "Should have a buffer now");
|
||||
|
||||
if (mDownscaler) {
|
||||
nsresult rv = mDownscaler->BeginFrame(GetSize(), Nothing(),
|
||||
mImageData, /* aHasAlpha = */ true);
|
||||
if (NS_FAILED(rv)) {
|
||||
return Transition::Terminate(State::FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
return Transition::To(State::ROW_OF_PIXELS, mBytesPerRow);
|
||||
}
|
||||
|
||||
LexerTransition<nsIconDecoder::State>
|
||||
nsIconDecoder::ReadRowOfPixels(const char* aData, size_t aLength)
|
||||
{
|
||||
if (mDownscaler) {
|
||||
memcpy(mDownscaler->RowBuffer(), aData, mBytesPerRow);
|
||||
mDownscaler->CommitRow();
|
||||
|
||||
if (mDownscaler->HasInvalidation()) {
|
||||
DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
|
||||
PostInvalidation(invalidRect.mOriginalSizeRect,
|
||||
Some(invalidRect.mTargetSizeRect));
|
||||
}
|
||||
} else {
|
||||
memcpy(mImageData + mCurrentRow * mBytesPerRow, aData, mBytesPerRow);
|
||||
|
||||
PostInvalidation(IntRect(0, mCurrentRow, mWidth, 1));
|
||||
}
|
||||
mCurrentRow++;
|
||||
|
||||
return (mCurrentRow < mHeight)
|
||||
? Transition::To(State::ROW_OF_PIXELS, mBytesPerRow)
|
||||
: Transition::To(State::FINISH, 0);
|
||||
}
|
||||
|
||||
LexerTransition<nsIconDecoder::State>
|
||||
nsIconDecoder::Finish()
|
||||
{
|
||||
PostFrameStop();
|
||||
PostDecodeDone();
|
||||
|
||||
return Transition::Terminate(State::SUCCESS);
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
|
@ -8,7 +8,7 @@
|
||||
#define mozilla_image_decoders_nsIconDecoder_h
|
||||
|
||||
#include "Decoder.h"
|
||||
|
||||
#include "StreamingLexer.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -47,18 +47,24 @@ private:
|
||||
// Decoders should only be instantiated via DecoderFactory.
|
||||
explicit nsIconDecoder(RasterImage* aImage);
|
||||
|
||||
uint32_t mExpectedDataLength;
|
||||
uint32_t mPixBytesRead;
|
||||
uint32_t mState;
|
||||
enum class State {
|
||||
HEADER,
|
||||
ROW_OF_PIXELS,
|
||||
FINISH,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
};
|
||||
|
||||
LexerTransition<State> ReadHeader(const char* aData);
|
||||
LexerTransition<State> ReadRowOfPixels(const char* aData, size_t aLength);
|
||||
LexerTransition<State> Finish();
|
||||
|
||||
StreamingLexer<State> mLexer;
|
||||
uint8_t mWidth;
|
||||
uint8_t mHeight;
|
||||
};
|
||||
|
||||
enum {
|
||||
iconStateStart = 0,
|
||||
iconStateHaveHeight = 1,
|
||||
iconStateReadPixels = 2,
|
||||
iconStateFinished = 3
|
||||
uint32_t mBytesPerRow;
|
||||
uint32_t mBytesTotal;
|
||||
uint32_t mCurrentRow;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
|
Loading…
Reference in New Issue
Block a user