mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 20:07:30 +09:00
Port WebP decoder changes.
This breaks animated WebP for the moment, but adds QCMS color management and lexer changes. Tag #831
This commit is contained in:
parent
b504989570
commit
dfe6efae2f
2 changed files with 408 additions and 273 deletions
|
|
@ -19,10 +19,6 @@ static LazyLogModule sWebPLog("WebPDecoder");
|
||||||
|
|
||||||
nsWebPDecoder::nsWebPDecoder(RasterImage* aImage)
|
nsWebPDecoder::nsWebPDecoder(RasterImage* aImage)
|
||||||
: Decoder(aImage)
|
: Decoder(aImage)
|
||||||
, mLexer(Transition::ToUnbuffered(State::FINISHED_WEBP_DATA,
|
|
||||||
State::WEBP_DATA,
|
|
||||||
SIZE_MAX),
|
|
||||||
Transition::TerminateSuccess())
|
|
||||||
, mDecoder(nullptr)
|
, mDecoder(nullptr)
|
||||||
, mBlend(BlendMethod::OVER)
|
, mBlend(BlendMethod::OVER)
|
||||||
, mDisposal(DisposalMethod::KEEP)
|
, mDisposal(DisposalMethod::KEEP)
|
||||||
|
|
@ -30,6 +26,13 @@ nsWebPDecoder::nsWebPDecoder(RasterImage* aImage)
|
||||||
, mFormat(SurfaceFormat::B8G8R8X8)
|
, mFormat(SurfaceFormat::B8G8R8X8)
|
||||||
, mLastRow(0)
|
, mLastRow(0)
|
||||||
, mCurrentFrame(0)
|
, mCurrentFrame(0)
|
||||||
|
, mData(nullptr)
|
||||||
|
, mLength(0)
|
||||||
|
, mIteratorComplete(false)
|
||||||
|
, mNeedDemuxer(true)
|
||||||
|
, mGotColorProfile(false)
|
||||||
|
, mInProfile(nullptr)
|
||||||
|
, mTransform(nullptr)
|
||||||
{
|
{
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||||
("[this=%p] nsWebPDecoder::nsWebPDecoder", this));
|
("[this=%p] nsWebPDecoder::nsWebPDecoder", this));
|
||||||
|
|
@ -39,27 +42,157 @@ nsWebPDecoder::~nsWebPDecoder()
|
||||||
{
|
{
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||||
("[this=%p] nsWebPDecoder::~nsWebPDecoder", this));
|
("[this=%p] nsWebPDecoder::~nsWebPDecoder", this));
|
||||||
WebPIDelete(mDecoder);
|
if (mDecoder) {
|
||||||
|
WebPIDelete(mDecoder);
|
||||||
|
WebPFreeDecBuffer(&mBuffer);
|
||||||
|
}
|
||||||
|
if (mInProfile) {
|
||||||
|
// mTransform belongs to us only if mInProfile is non-null
|
||||||
|
if (mTransform) {
|
||||||
|
qcms_transform_release(mTransform);
|
||||||
|
}
|
||||||
|
qcms_profile_release(mInProfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LexerResult
|
||||||
|
nsWebPDecoder::ReadData()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mData);
|
||||||
|
MOZ_ASSERT(mLength > 0);
|
||||||
|
|
||||||
|
WebPDemuxer* demuxer = nullptr;
|
||||||
|
bool complete = mIteratorComplete;
|
||||||
|
|
||||||
|
if (mNeedDemuxer) {
|
||||||
|
WebPDemuxState state;
|
||||||
|
WebPData fragment;
|
||||||
|
fragment.bytes = mData;
|
||||||
|
fragment.size = mLength;
|
||||||
|
|
||||||
|
demuxer = WebPDemuxPartial(&fragment, &state);
|
||||||
|
if (state == WEBP_DEMUX_PARSE_ERROR) {
|
||||||
|
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||||
|
("[this=%p] nsWebPDecoder::ReadData -- demux parse error\n", this));
|
||||||
|
WebPDemuxDelete(demuxer);
|
||||||
|
return LexerResult(TerminalState::FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == WEBP_DEMUX_PARSING_HEADER) {
|
||||||
|
WebPDemuxDelete(demuxer);
|
||||||
|
return LexerResult(Yield::NEED_MORE_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!demuxer) {
|
||||||
|
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||||
|
("[this=%p] nsWebPDecoder::ReadData -- no demuxer\n", this));
|
||||||
|
return LexerResult(TerminalState::FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
complete = complete || state == WEBP_DEMUX_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
LexerResult rv(TerminalState::FAILURE);
|
||||||
|
if (!HasSize()) {
|
||||||
|
rv = ReadHeader(demuxer, complete);
|
||||||
|
} else {
|
||||||
|
rv = ReadPayload(demuxer, complete);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebPDemuxDelete(demuxer);
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
LexerResult
|
LexerResult
|
||||||
nsWebPDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
|
nsWebPDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
SourceBufferIterator::State state = SourceBufferIterator::COMPLETE;
|
||||||
|
if (!mIteratorComplete) {
|
||||||
|
state = aIterator.AdvanceOrScheduleResume(SIZE_MAX, aOnResume);
|
||||||
|
|
||||||
|
// We need to remember since we can't advance a complete iterator.
|
||||||
|
mIteratorComplete = state == SourceBufferIterator::COMPLETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == SourceBufferIterator::WAITING) {
|
||||||
|
return LexerResult(Yield::NEED_MORE_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
LexerResult rv = UpdateBuffer(aIterator, state);
|
||||||
|
if (rv.is<Yield>() && rv.as<Yield>() == Yield::NEED_MORE_DATA) {
|
||||||
|
// We need to check the iterator to see if more is available before
|
||||||
|
// giving up unless we are already complete.
|
||||||
|
if (mIteratorComplete) {
|
||||||
|
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||||
|
("[this=%p] nsWebPDecoder::DoDecode -- read all data, "
|
||||||
|
"but needs more\n", this));
|
||||||
|
return LexerResult(TerminalState::FAILURE);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LexerResult
|
||||||
|
nsWebPDecoder::UpdateBuffer(SourceBufferIterator& aIterator,
|
||||||
|
SourceBufferIterator::State aState)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
|
MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
|
||||||
|
|
||||||
return mLexer.Lex(aIterator, aOnResume,
|
switch (aState) {
|
||||||
[=](State aState, const char* aData, size_t aLength) {
|
case SourceBufferIterator::READY:
|
||||||
switch (aState) {
|
if (!mData) {
|
||||||
case State::WEBP_DATA:
|
// For as long as we hold onto an iterator, we know the data pointers
|
||||||
if (!HasSize()) {
|
// to the chunks cannot change underneath us, so save the pointer to
|
||||||
return ReadHeader(aData, aLength);
|
// the first block.
|
||||||
}
|
MOZ_ASSERT(mLength == 0);
|
||||||
return ReadPayload(aData, aLength);
|
mData = reinterpret_cast<const uint8_t*>(aIterator.Data());
|
||||||
case State::FINISHED_WEBP_DATA:
|
}
|
||||||
return FinishedData();
|
mLength += aIterator.Length();
|
||||||
|
return ReadData();
|
||||||
|
case SourceBufferIterator::COMPLETE:
|
||||||
|
return ReadData();
|
||||||
|
default:
|
||||||
|
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||||
|
("[this=%p] nsWebPDecoder::DoDecode -- bad state\n", this));
|
||||||
|
return LexerResult(TerminalState::FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to buffer. If we have no data buffered, we need to get everything
|
||||||
|
// from the first chunk of the source buffer before appending the new data.
|
||||||
|
if (mBufferedData.empty()) {
|
||||||
|
MOZ_ASSERT(mData);
|
||||||
|
MOZ_ASSERT(mLength > 0);
|
||||||
|
|
||||||
|
if (!mBufferedData.append(mData, mLength)) {
|
||||||
|
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||||
|
("[this=%p] nsWebPDecoder::DoDecode -- oom, initialize %zu\n",
|
||||||
|
this, mLength));
|
||||||
|
return LexerResult(TerminalState::FAILURE);
|
||||||
}
|
}
|
||||||
MOZ_CRASH("Unknown State");
|
|
||||||
});
|
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||||
|
("[this=%p] nsWebPDecoder::DoDecode -- buffered %zu bytes\n",
|
||||||
|
this, mLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the incremental data from the iterator.
|
||||||
|
if (!mBufferedData.append(aIterator.Data(), aIterator.Length())) {
|
||||||
|
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||||
|
("[this=%p] nsWebPDecoder::DoDecode -- oom, append %zu on %zu\n",
|
||||||
|
this, aIterator.Length(), mBufferedData.length()));
|
||||||
|
return LexerResult(TerminalState::FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||||
|
("[this=%p] nsWebPDecoder::DoDecode -- buffered %zu -> %zu bytes\n",
|
||||||
|
this, aIterator.Length(), mBufferedData.length()));
|
||||||
|
mData = mBufferedData.begin();
|
||||||
|
mLength = mBufferedData.length();
|
||||||
|
return ReadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
|
@ -69,13 +202,22 @@ nsWebPDecoder::CreateFrame(const nsIntRect& aFrameRect)
|
||||||
MOZ_ASSERT(!mDecoder);
|
MOZ_ASSERT(!mDecoder);
|
||||||
|
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||||
("[this=%p] nsWebPDecoder::CreateFrame -- frame %u, %d x %d\n",
|
("[this=%p] nsWebPDecoder::CreateFrame -- frame %u, (%d, %d) %d x %d\n",
|
||||||
this, mCurrentFrame, aFrameRect.width, aFrameRect.height));
|
this, mCurrentFrame, aFrameRect.x, aFrameRect.y,
|
||||||
|
aFrameRect.width, aFrameRect.height));
|
||||||
|
|
||||||
|
if (aFrameRect.width <= 0 || aFrameRect.height <= 0) {
|
||||||
|
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||||
|
("[this=%p] nsWebPDecoder::CreateFrame -- bad frame rect\n",
|
||||||
|
this));
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
// If this is our first frame in an animation and it doesn't cover the
|
// If this is our first frame in an animation and it doesn't cover the
|
||||||
// full frame, then we are transparent even if there is no alpha
|
// full frame, then we are transparent even if there is no alpha
|
||||||
if (mCurrentFrame == 0 && !aFrameRect.IsEqualEdges(FullFrame())) {
|
if (mCurrentFrame == 0 && !aFrameRect.IsEqualEdges(FullFrame())) {
|
||||||
MOZ_ASSERT(HasAnimation());
|
MOZ_ASSERT(HasAnimation());
|
||||||
|
mFormat = SurfaceFormat::B8G8R8A8;
|
||||||
PostHasTransparency();
|
PostHasTransparency();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,16 +232,18 @@ nsWebPDecoder::CreateFrame(const nsIntRect& aFrameRect)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SurfacePipeFlags pipeFlags = SurfacePipeFlags();
|
||||||
|
|
||||||
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(this,
|
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(this,
|
||||||
mCurrentFrame, Size(), OutputSize(), aFrameRect,
|
mCurrentFrame, Size(), OutputSize(), aFrameRect, mFormat, pipeFlags);
|
||||||
mFormat, SurfacePipeFlags());
|
|
||||||
if (!pipe) {
|
if (!pipe) {
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||||
("[this=%p] nsWebPDecoder::CreateFrame -- no pipe\n", this));
|
("[this=%p] nsWebPDecoder::CreateFrame -- no pipe\n", this));
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
mPipe = Move(*pipe);
|
mFrameRect = aFrameRect;
|
||||||
|
mPipe = std::move(*pipe);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,154 +262,149 @@ nsWebPDecoder::EndFrame()
|
||||||
this, mCurrentFrame, (int)opacity, (int)mDisposal,
|
this, mCurrentFrame, (int)opacity, (int)mDisposal,
|
||||||
mTimeout.AsEncodedValueDeprecated(), (int)mBlend));
|
mTimeout.AsEncodedValueDeprecated(), (int)mBlend));
|
||||||
|
|
||||||
PostFrameStop(opacity, mDisposal, mTimeout, mBlend);
|
PostFrameStop(opacity);
|
||||||
WebPFreeDecBuffer(&mBuffer);
|
|
||||||
WebPIDelete(mDecoder);
|
WebPIDelete(mDecoder);
|
||||||
|
WebPFreeDecBuffer(&mBuffer);
|
||||||
mDecoder = nullptr;
|
mDecoder = nullptr;
|
||||||
mLastRow = 0;
|
mLastRow = 0;
|
||||||
++mCurrentFrame;
|
++mCurrentFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
void
|
||||||
nsWebPDecoder::GetDataBuffer(const uint8_t*& aData, size_t& aLength)
|
nsWebPDecoder::ApplyColorProfile(const char* aProfile, size_t aLength)
|
||||||
{
|
{
|
||||||
if (!mData.empty() && mData.begin() != aData) {
|
MOZ_ASSERT(!mGotColorProfile);
|
||||||
if (!mData.append(aData, aLength)) {
|
mGotColorProfile = true;
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
|
||||||
("[this=%p] nsWebPDecoder::GetDataBuffer -- oom, append %zu on %zu\n",
|
|
||||||
this, aLength, mData.length()));
|
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
aData = mData.begin();
|
|
||||||
aLength = mData.length();
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
if (GetSurfaceFlags() & SurfaceFlags::NO_COLORSPACE_CONVERSION) {
|
||||||
nsWebPDecoder::SaveDataBuffer(const uint8_t* aData, size_t aLength)
|
return;
|
||||||
{
|
}
|
||||||
if (mData.empty() && !mData.append(aData, aLength)) {
|
|
||||||
|
auto mode = gfxPlatform::GetCMSMode();
|
||||||
|
if (mode == eCMSMode_Off || (mode == eCMSMode_TaggedOnly && !aProfile)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aProfile || !gfxPlatform::GetCMSOutputProfile()) {
|
||||||
|
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||||
|
("[this=%p] nsWebPDecoder::ApplyColorProfile -- not tagged or no output "
|
||||||
|
"profile , use sRGB transform\n", this));
|
||||||
|
mTransform = gfxPlatform::GetCMSRGBATransform();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mInProfile = qcms_profile_from_memory(aProfile, aLength);
|
||||||
|
if (!mInProfile) {
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||||
("[this=%p] nsWebPDecoder::SaveDataBuffer -- oom, append %zu on %zu\n",
|
("[this=%p] nsWebPDecoder::ApplyColorProfile -- bad color profile\n",
|
||||||
this, aLength, mData.length()));
|
this));
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return;
|
||||||
}
|
}
|
||||||
return NS_OK;
|
|
||||||
|
// Calculate rendering intent.
|
||||||
|
int intent = gfxPlatform::GetRenderingIntent();
|
||||||
|
if (intent == -1) {
|
||||||
|
intent = qcms_profile_get_rendering_intent(mInProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the color management transform.
|
||||||
|
mTransform = qcms_transform_create(mInProfile,
|
||||||
|
QCMS_DATA_RGBA_8,
|
||||||
|
gfxPlatform::GetCMSOutputProfile(),
|
||||||
|
QCMS_DATA_RGBA_8,
|
||||||
|
(qcms_intent)intent);
|
||||||
|
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||||
|
("[this=%p] nsWebPDecoder::ApplyColorProfile -- use tagged "
|
||||||
|
"transform\n", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
LexerTransition<nsWebPDecoder::State>
|
LexerResult
|
||||||
nsWebPDecoder::ReadHeader(const char* aData, size_t aLength)
|
nsWebPDecoder::ReadHeader(WebPDemuxer* aDemuxer,
|
||||||
|
bool aIsComplete)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(aDemuxer);
|
||||||
|
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||||
("[this=%p] nsWebPDecoder::ReadHeader -- %zu bytes\n", this, aLength));
|
("[this=%p] nsWebPDecoder::ReadHeader -- %zu bytes\n", this, mLength));
|
||||||
|
|
||||||
// XXX(aosmond): In an ideal world, we could request the lexer to do this
|
uint32_t flags = WebPDemuxGetI(aDemuxer, WEBP_FF_FORMAT_FLAGS);
|
||||||
// buffering for us (and in turn the underlying SourceBuffer). That way we
|
|
||||||
// could avoid extra copies during the decode and just do
|
|
||||||
// SourceBuffer::Compact on each iteration. For a typical WebP image we
|
|
||||||
// can hope that we will get the full header in the first packet, but
|
|
||||||
// for animated images we will end up buffering the whole stream if it
|
|
||||||
// not already fully received and contiguous.
|
|
||||||
auto data = (const uint8_t*)aData;
|
|
||||||
size_t length = aLength;
|
|
||||||
if (NS_FAILED(GetDataBuffer(data, length))) {
|
|
||||||
return Transition::TerminateFailure();
|
|
||||||
}
|
|
||||||
|
|
||||||
WebPBitstreamFeatures features;
|
if (!IsMetadataDecode() && !mGotColorProfile) {
|
||||||
VP8StatusCode status = WebPGetFeatures(data, length, &features);
|
if (flags & WebPFeatureFlags::ICCP_FLAG) {
|
||||||
switch (status) {
|
WebPChunkIterator iter;
|
||||||
case VP8_STATUS_OK:
|
if (!WebPDemuxGetChunk(aDemuxer, "ICCP", 1, &iter)) {
|
||||||
break;
|
return aIsComplete ? LexerResult(TerminalState::FAILURE)
|
||||||
case VP8_STATUS_NOT_ENOUGH_DATA:
|
: LexerResult(Yield::NEED_MORE_DATA);
|
||||||
if (NS_FAILED(SaveDataBuffer(data, length))) {
|
|
||||||
return Transition::TerminateFailure();
|
|
||||||
}
|
}
|
||||||
return Transition::ContinueUnbuffered(State::WEBP_DATA);
|
|
||||||
default:
|
ApplyColorProfile(reinterpret_cast<const char*>(iter.chunk.bytes),
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
iter.chunk.size);
|
||||||
("[this=%p] nsWebPDecoder::ReadHeader -- parse error %d\n",
|
WebPDemuxReleaseChunkIterator(&iter);
|
||||||
this, status));
|
} else {
|
||||||
return Transition::TerminateFailure();
|
ApplyColorProfile(nullptr, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (features.has_animation) {
|
if (flags & WebPFeatureFlags::ANIMATION_FLAG) {
|
||||||
// A metadata decode expects to get the correct first frame timeout which
|
// A metadata decode expects to get the correct first frame timeout which
|
||||||
// sadly is not provided by the normal WebP header parsing.
|
// sadly is not provided by the normal WebP header parsing.
|
||||||
WebPDemuxState state;
|
|
||||||
WebPData fragment;
|
|
||||||
fragment.bytes = data;
|
|
||||||
fragment.size = length;
|
|
||||||
WebPDemuxer* demuxer = WebPDemuxPartial(&fragment, &state);
|
|
||||||
if (!demuxer || state == WEBP_DEMUX_PARSE_ERROR) {
|
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
|
||||||
("[this=%p] nsWebPDecoder::ReadHeader -- demux parse error\n", this));
|
|
||||||
WebPDemuxDelete(demuxer);
|
|
||||||
return Transition::TerminateFailure();
|
|
||||||
}
|
|
||||||
|
|
||||||
WebPIterator iter;
|
WebPIterator iter;
|
||||||
if (!WebPDemuxGetFrame(demuxer, 1, &iter)) {
|
if (!WebPDemuxGetFrame(aDemuxer, 1, &iter)) {
|
||||||
WebPDemuxDelete(demuxer);
|
return aIsComplete ? LexerResult(TerminalState::FAILURE)
|
||||||
if (state == WEBP_DEMUX_DONE) {
|
: LexerResult(Yield::NEED_MORE_DATA);
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
|
||||||
("[this=%p] nsWebPDecoder::ReadHeader -- demux parse error\n",
|
|
||||||
this));
|
|
||||||
return Transition::TerminateFailure();
|
|
||||||
}
|
|
||||||
if (NS_FAILED(SaveDataBuffer(data, length))) {
|
|
||||||
return Transition::TerminateFailure();
|
|
||||||
}
|
|
||||||
return Transition::ContinueUnbuffered(State::WEBP_DATA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PostIsAnimated(FrameTimeout::FromRawMilliseconds(iter.duration));
|
PostIsAnimated(FrameTimeout::FromRawMilliseconds(iter.duration));
|
||||||
WebPDemuxReleaseIterator(&iter);
|
WebPDemuxReleaseIterator(&iter);
|
||||||
WebPDemuxDelete(demuxer);
|
} else {
|
||||||
|
// Single frames don't need a demuxer to be created.
|
||||||
|
mNeedDemuxer = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
uint32_t width = WebPDemuxGetI(aDemuxer, WEBP_FF_CANVAS_WIDTH);
|
||||||
("[this=%p] nsWebPDecoder::ReadHeader -- %d x %d, alpha %d, "
|
uint32_t height = WebPDemuxGetI(aDemuxer, WEBP_FF_CANVAS_HEIGHT);
|
||||||
"animation %d, format %d, metadata decode %d, first frame decode %d\n",
|
if (width > INT32_MAX || height > INT32_MAX) {
|
||||||
this, features.width, features.height, features.has_alpha,
|
return LexerResult(TerminalState::FAILURE);
|
||||||
features.has_animation, features.format, IsMetadataDecode(),
|
}
|
||||||
IsFirstFrameDecode()));
|
|
||||||
|
|
||||||
PostSize(features.width, features.height);
|
PostSize(width, height);
|
||||||
if (features.has_alpha) {
|
|
||||||
|
bool alpha = flags & WebPFeatureFlags::ALPHA_FLAG;
|
||||||
|
if (alpha) {
|
||||||
mFormat = SurfaceFormat::B8G8R8A8;
|
mFormat = SurfaceFormat::B8G8R8A8;
|
||||||
PostHasTransparency();
|
PostHasTransparency();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||||
|
("[this=%p] nsWebPDecoder::ReadHeader -- %u x %u, alpha %d, "
|
||||||
|
"animation %d, metadata decode %d, first frame decode %d\n",
|
||||||
|
this, width, height, alpha, HasAnimation(),
|
||||||
|
IsMetadataDecode(), IsFirstFrameDecode()));
|
||||||
|
|
||||||
if (IsMetadataDecode()) {
|
if (IsMetadataDecode()) {
|
||||||
return Transition::TerminateSuccess();
|
return LexerResult(TerminalState::SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto transition = ReadPayload((const char*)data, length);
|
return ReadPayload(aDemuxer, aIsComplete);
|
||||||
if (!features.has_animation) {
|
|
||||||
mData.clearAndFree();
|
|
||||||
}
|
|
||||||
return transition;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LexerTransition<nsWebPDecoder::State>
|
LexerResult
|
||||||
nsWebPDecoder::ReadPayload(const char* aData, size_t aLength)
|
nsWebPDecoder::ReadPayload(WebPDemuxer* aDemuxer,
|
||||||
|
bool aIsComplete)
|
||||||
{
|
{
|
||||||
auto data = (const uint8_t*)aData;
|
|
||||||
if (!HasAnimation()) {
|
if (!HasAnimation()) {
|
||||||
auto rv = ReadSingle(data, aLength, true, FullFrame());
|
auto rv = ReadSingle(mData, mLength, FullFrame());
|
||||||
if (rv.NextStateIsTerminal() &&
|
if (rv.is<TerminalState>() &&
|
||||||
rv.NextStateAsTerminal() == TerminalState::SUCCESS) {
|
rv.as<TerminalState>() == TerminalState::SUCCESS) {
|
||||||
PostDecodeDone();
|
PostDecodeDone();
|
||||||
}
|
}
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
return ReadMultiple(data, aLength);
|
return ReadMultiple(aDemuxer, aIsComplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
LexerTransition<nsWebPDecoder::State>
|
LexerResult
|
||||||
nsWebPDecoder::ReadSingle(const uint8_t* aData, size_t aLength, bool aAppend, const IntRect& aFrameRect)
|
nsWebPDecoder::ReadSingle(const uint8_t* aData, size_t aLength, const IntRect& aFrameRect)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!IsMetadataDecode());
|
MOZ_ASSERT(!IsMetadataDecode());
|
||||||
MOZ_ASSERT(aData);
|
MOZ_ASSERT(aData);
|
||||||
|
|
@ -275,125 +414,120 @@ nsWebPDecoder::ReadSingle(const uint8_t* aData, size_t aLength, bool aAppend, co
|
||||||
("[this=%p] nsWebPDecoder::ReadSingle -- %zu bytes\n", this, aLength));
|
("[this=%p] nsWebPDecoder::ReadSingle -- %zu bytes\n", this, aLength));
|
||||||
|
|
||||||
if (!mDecoder && NS_FAILED(CreateFrame(aFrameRect))) {
|
if (!mDecoder && NS_FAILED(CreateFrame(aFrameRect))) {
|
||||||
return Transition::TerminateFailure();
|
return LexerResult(TerminalState::FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX(aosmond): The demux API can be used for single images according to the
|
|
||||||
// documentation. If WebPIAppend is not any more efficient in its buffering
|
|
||||||
// than what we do for animated images, we should just combine the use cases.
|
|
||||||
bool complete;
|
bool complete;
|
||||||
VP8StatusCode status;
|
do {
|
||||||
if (aAppend) {
|
VP8StatusCode status = WebPIUpdate(mDecoder, aData, aLength);
|
||||||
status = WebPIAppend(mDecoder, aData, aLength);
|
switch (status) {
|
||||||
} else {
|
case VP8_STATUS_OK:
|
||||||
status = WebPIUpdate(mDecoder, aData, aLength);
|
complete = true;
|
||||||
}
|
break;
|
||||||
switch (status) {
|
case VP8_STATUS_SUSPENDED:
|
||||||
case VP8_STATUS_OK:
|
complete = false;
|
||||||
complete = true;
|
break;
|
||||||
break;
|
default:
|
||||||
case VP8_STATUS_SUSPENDED:
|
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||||
complete = false;
|
("[this=%p] nsWebPDecoder::ReadSingle -- append error %d\n",
|
||||||
break;
|
this, status));
|
||||||
default:
|
return LexerResult(TerminalState::FAILURE);
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
}
|
||||||
("[this=%p] nsWebPDecoder::ReadSingle -- append error %d\n",
|
|
||||||
this, status));
|
int lastRow = -1;
|
||||||
return Transition::TerminateFailure();
|
int width = 0;
|
||||||
}
|
int height = 0;
|
||||||
|
int stride = 0;
|
||||||
int lastRow = -1;
|
uint8_t* rowStart = WebPIDecGetRGB(mDecoder, &lastRow, &width, &height, &stride);
|
||||||
int width = 0;
|
|
||||||
int height = 0;
|
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||||
int stride = 0;
|
("[this=%p] nsWebPDecoder::ReadSingle -- complete %d, read %d rows, "
|
||||||
const uint8_t* rowStart = WebPIDecGetRGB(mDecoder, &lastRow, &width, &height, &stride);
|
"has %d rows available\n", this, complete, mLastRow, lastRow));
|
||||||
if (!rowStart || lastRow == -1) {
|
|
||||||
return Transition::ContinueUnbuffered(State::WEBP_DATA);
|
if (!rowStart || lastRow == -1 || lastRow == mLastRow) {
|
||||||
}
|
return LexerResult(Yield::NEED_MORE_DATA);
|
||||||
|
}
|
||||||
if (width <= 0 || height <= 0 || stride <= 0) {
|
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
if (width != mFrameRect.width || height != mFrameRect.height ||
|
||||||
("[this=%p] nsWebPDecoder::ReadSingle -- bad (w,h,s) = (%d, %d, %d)\n",
|
stride < mFrameRect.width * 4 ||
|
||||||
this, width, height, stride));
|
lastRow > mFrameRect.height) {
|
||||||
return Transition::TerminateFailure();
|
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||||
}
|
("[this=%p] nsWebPDecoder::ReadSingle -- bad (w,h,s) = (%d, %d, %d)\n",
|
||||||
|
this, width, height, stride));
|
||||||
for (int row = mLastRow; row < lastRow; row++) {
|
return LexerResult(TerminalState::FAILURE);
|
||||||
const uint8_t* src = rowStart + row * stride;
|
}
|
||||||
auto result = mPipe.WritePixelsToRow<uint32_t>([&]() -> NextPixel<uint32_t> {
|
|
||||||
MOZ_ASSERT(mFormat == SurfaceFormat::B8G8R8A8 || src[3] == 0xFF);
|
const bool noPremultiply =
|
||||||
const uint32_t pixel = gfxPackedPixel(src[3], src[0], src[1], src[2]);
|
bool(GetSurfaceFlags() & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
|
||||||
src += 4;
|
|
||||||
return AsVariant(pixel);
|
for (int row = mLastRow; row < lastRow; row++) {
|
||||||
});
|
uint8_t* src = rowStart + row * stride;
|
||||||
MOZ_ASSERT(result != WriteState::FAILURE);
|
if (mTransform) {
|
||||||
MOZ_ASSERT_IF(result == WriteState::FINISHED, complete && row == lastRow - 1);
|
qcms_transform_data(mTransform, src, src, width);
|
||||||
|
}
|
||||||
if (result == WriteState::FAILURE) {
|
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
WriteState result;
|
||||||
("[this=%p] nsWebPDecoder::ReadSingle -- write pixels error\n",
|
if (noPremultiply) {
|
||||||
this));
|
result = mPipe.WritePixelsToRow<uint32_t>([&]() -> NextPixel<uint32_t> {
|
||||||
return Transition::TerminateFailure();
|
MOZ_ASSERT(mFormat == SurfaceFormat::B8G8R8A8 || src[3] == 0xFF);
|
||||||
|
const uint32_t pixel =
|
||||||
|
gfxPackedPixelNoPreMultiply(src[3], src[0], src[1], src[2]);
|
||||||
|
src += 4;
|
||||||
|
return AsVariant(pixel);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result = mPipe.WritePixelsToRow<uint32_t>([&]() -> NextPixel<uint32_t> {
|
||||||
|
MOZ_ASSERT(mFormat == SurfaceFormat::B8G8R8A8 || src[3] == 0xFF);
|
||||||
|
const uint32_t pixel = gfxPackedPixel(src[3], src[0], src[1], src[2]);
|
||||||
|
src += 4;
|
||||||
|
return AsVariant(pixel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Maybe<SurfaceInvalidRect> invalidRect = mPipe.TakeInvalidRect();
|
||||||
|
if (invalidRect) {
|
||||||
|
PostInvalidation(invalidRect->mInputSpaceRect,
|
||||||
|
Some(invalidRect->mOutputSpaceRect));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == WriteState::FAILURE) {
|
||||||
|
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||||
|
("[this=%p] nsWebPDecoder::ReadSingle -- write pixels error\n",
|
||||||
|
this));
|
||||||
|
return LexerResult(TerminalState::FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == WriteState::FINISHED) {
|
||||||
|
MOZ_ASSERT(row == lastRow - 1, "There was more data to read?");
|
||||||
|
complete = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (mLastRow != lastRow) {
|
|
||||||
mLastRow = lastRow;
|
mLastRow = lastRow;
|
||||||
|
} while (!complete);
|
||||||
Maybe<SurfaceInvalidRect> invalidRect = mPipe.TakeInvalidRect();
|
|
||||||
if (invalidRect) {
|
|
||||||
PostInvalidation(invalidRect->mInputSpaceRect,
|
|
||||||
Some(invalidRect->mOutputSpaceRect));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!complete) {
|
if (!complete) {
|
||||||
return Transition::ContinueUnbuffered(State::WEBP_DATA);
|
return LexerResult(Yield::NEED_MORE_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
EndFrame();
|
EndFrame();
|
||||||
return Transition::TerminateSuccess();
|
return LexerResult(TerminalState::SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
LexerTransition<nsWebPDecoder::State>
|
LexerResult
|
||||||
nsWebPDecoder::ReadMultiple(const uint8_t* aData, size_t aLength)
|
nsWebPDecoder::ReadMultiple(WebPDemuxer* aDemuxer, bool aIsComplete)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!IsMetadataDecode());
|
MOZ_ASSERT(!IsMetadataDecode());
|
||||||
MOZ_ASSERT(aData);
|
MOZ_ASSERT(aDemuxer);
|
||||||
|
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||||
("[this=%p] nsWebPDecoder::ReadMultiple -- %zu bytes\n", this, aLength));
|
("[this=%p] nsWebPDecoder::ReadMultiple\n", this));
|
||||||
|
|
||||||
auto data = aData;
|
bool complete = aIsComplete;
|
||||||
size_t length = aLength;
|
|
||||||
if (NS_FAILED(GetDataBuffer(data, length))) {
|
|
||||||
return Transition::TerminateFailure();
|
|
||||||
}
|
|
||||||
|
|
||||||
WebPDemuxState state;
|
|
||||||
WebPData fragment;
|
|
||||||
fragment.bytes = data;
|
|
||||||
fragment.size = length;
|
|
||||||
WebPDemuxer* demuxer = WebPDemuxPartial(&fragment, &state);
|
|
||||||
if (!demuxer) {
|
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
|
||||||
("[this=%p] nsWebPDecoder::ReadMultiple -- create demuxer error\n",
|
|
||||||
this));
|
|
||||||
return Transition::TerminateFailure();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == WEBP_DEMUX_PARSE_ERROR) {
|
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
|
||||||
("[this=%p] nsWebPDecoder::ReadMultiple -- demuxer parse error\n",
|
|
||||||
this));
|
|
||||||
WebPDemuxDelete(demuxer);
|
|
||||||
return Transition::TerminateFailure();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool complete = false;
|
|
||||||
WebPIterator iter;
|
WebPIterator iter;
|
||||||
auto rv = Transition::ContinueUnbuffered(State::WEBP_DATA);
|
auto rv = LexerResult(Yield::NEED_MORE_DATA);
|
||||||
if (WebPDemuxGetFrame(demuxer, mCurrentFrame + 1, &iter)) {
|
if (WebPDemuxGetFrame(aDemuxer, mCurrentFrame + 1, &iter)) {
|
||||||
switch (iter.blend_method) {
|
switch (iter.blend_method) {
|
||||||
case WEBP_MUX_BLEND:
|
case WEBP_MUX_BLEND:
|
||||||
mBlend = BlendMethod::OVER;
|
mBlend = BlendMethod::OVER;
|
||||||
|
|
@ -418,51 +552,34 @@ nsWebPDecoder::ReadMultiple(const uint8_t* aData, size_t aLength)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
mFormat = iter.has_alpha ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
|
mFormat = iter.has_alpha || mCurrentFrame > 0 ? SurfaceFormat::B8G8R8A8
|
||||||
|
: SurfaceFormat::B8G8R8X8;
|
||||||
mTimeout = FrameTimeout::FromRawMilliseconds(iter.duration);
|
mTimeout = FrameTimeout::FromRawMilliseconds(iter.duration);
|
||||||
nsIntRect frameRect(iter.x_offset, iter.y_offset, iter.width, iter.height);
|
nsIntRect frameRect(iter.x_offset, iter.y_offset, iter.width, iter.height);
|
||||||
|
|
||||||
rv = ReadSingle(iter.fragment.bytes, iter.fragment.size, false, frameRect);
|
rv = ReadSingle(iter.fragment.bytes, iter.fragment.size, frameRect);
|
||||||
complete = state == WEBP_DEMUX_DONE && !WebPDemuxNextFrame(&iter);
|
complete = complete && !WebPDemuxNextFrame(&iter);
|
||||||
WebPDemuxReleaseIterator(&iter);
|
WebPDemuxReleaseIterator(&iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rv.NextStateIsTerminal()) {
|
if (rv.is<TerminalState>() &&
|
||||||
if (rv.NextStateAsTerminal() == TerminalState::SUCCESS) {
|
rv.as<TerminalState>() == TerminalState::SUCCESS) {
|
||||||
// If we extracted one frame, and it is not the last, we need to yield to
|
// If we extracted one frame, and it is not the last, we need to yield to
|
||||||
// the lexer to allow the upper layers to acknowledge the frame.
|
// the lexer to allow the upper layers to acknowledge the frame.
|
||||||
if (!complete && !IsFirstFrameDecode()) {
|
if (!complete && !IsFirstFrameDecode()) {
|
||||||
// The resume point is determined by whether or not we had to buffer.
|
rv = LexerResult(Yield::OUTPUT_AVAILABLE);
|
||||||
// If we have yet to buffer, we want to resume at the same point,
|
} else {
|
||||||
// otherwise our internal buffer has everything we need and we want
|
uint32_t loopCount = WebPDemuxGetI(aDemuxer, WEBP_FF_LOOP_COUNT);
|
||||||
// to resume having consumed all of the current fragment.
|
|
||||||
rv = Transition::ContinueUnbufferedAfterYield(State::WEBP_DATA,
|
|
||||||
mData.empty() ? 0 : aLength);
|
|
||||||
} else {
|
|
||||||
uint32_t loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT);
|
|
||||||
|
|
||||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||||
("[this=%p] nsWebPDecoder::ReadMultiple -- loop count %u\n",
|
("[this=%p] nsWebPDecoder::ReadMultiple -- loop count %u\n",
|
||||||
this, loopCount));
|
this, loopCount));
|
||||||
PostDecodeDone(loopCount - 1);
|
PostDecodeDone(loopCount - 1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (NS_FAILED(SaveDataBuffer(data, length))) {
|
|
||||||
rv = Transition::TerminateFailure();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPDemuxDelete(demuxer);
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
LexerTransition<nsWebPDecoder::State>
|
|
||||||
nsWebPDecoder::FinishedData()
|
|
||||||
{
|
|
||||||
// Since we set up an unbuffered read for SIZE_MAX bytes, if we actually read
|
|
||||||
// all that data something is really wrong.
|
|
||||||
MOZ_ASSERT_UNREACHABLE("Read the entire address space?");
|
|
||||||
return Transition::TerminateFailure();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace image
|
} // namespace image
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace mozilla {
|
||||||
namespace image {
|
namespace image {
|
||||||
class RasterImage;
|
class RasterImage;
|
||||||
|
|
||||||
class nsWebPDecoder : public Decoder
|
class nsWebPDecoder final : public Decoder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~nsWebPDecoder();
|
virtual ~nsWebPDecoder();
|
||||||
|
|
@ -31,34 +31,28 @@ private:
|
||||||
// Decoders should only be instantiated via DecoderFactory.
|
// Decoders should only be instantiated via DecoderFactory.
|
||||||
explicit nsWebPDecoder(RasterImage* aImage);
|
explicit nsWebPDecoder(RasterImage* aImage);
|
||||||
|
|
||||||
enum class State
|
void ApplyColorProfile(const char* aProfile, size_t aLength);
|
||||||
{
|
|
||||||
WEBP_DATA,
|
|
||||||
FINISHED_WEBP_DATA
|
|
||||||
};
|
|
||||||
|
|
||||||
LexerTransition<State> ReadHeader(const char* aData, size_t aLength);
|
LexerResult UpdateBuffer(SourceBufferIterator& aIterator,
|
||||||
LexerTransition<State> ReadPayload(const char* aData, size_t aLength);
|
SourceBufferIterator::State aState);
|
||||||
LexerTransition<State> FinishedData();
|
LexerResult ReadData();
|
||||||
|
LexerResult ReadHeader(WebPDemuxer* aDemuxer, bool aIsComplete);
|
||||||
|
LexerResult ReadPayload(WebPDemuxer* aDemuxer, bool aIsComplete);
|
||||||
|
|
||||||
nsresult CreateFrame(const nsIntRect& aFrameRect);
|
nsresult CreateFrame(const nsIntRect& aFrameRect);
|
||||||
void EndFrame();
|
void EndFrame();
|
||||||
|
|
||||||
nsresult GetDataBuffer(const uint8_t*& aData, size_t& aLength);
|
LexerResult ReadSingle(const uint8_t* aData, size_t aLength,
|
||||||
nsresult SaveDataBuffer(const uint8_t* aData, size_t aLength);
|
const IntRect& aFrameRect);
|
||||||
|
|
||||||
LexerTransition<State> ReadSingle(const uint8_t* aData, size_t aLength,
|
LexerResult ReadMultiple(WebPDemuxer* aDemuxer, bool aIsComplete);
|
||||||
bool aAppend, const IntRect& aFrameRect);
|
|
||||||
|
|
||||||
LexerTransition<State> ReadMultiple(const uint8_t* aData, size_t aLength);
|
|
||||||
|
|
||||||
StreamingLexer<State> mLexer;
|
|
||||||
|
|
||||||
/// The SurfacePipe used to write to the output surface.
|
/// The SurfacePipe used to write to the output surface.
|
||||||
SurfacePipe mPipe;
|
SurfacePipe mPipe;
|
||||||
|
|
||||||
/// The buffer used to accumulate data until the complete WebP header is received.
|
/// The buffer used to accumulate data until the complete WebP header is
|
||||||
Vector<uint8_t> mData;
|
/// received, if and only if the iterator is discontiguous.
|
||||||
|
Vector<uint8_t> mBufferedData;
|
||||||
|
|
||||||
/// The libwebp output buffer descriptor pointing to the decoded data.
|
/// The libwebp output buffer descriptor pointing to the decoded data.
|
||||||
WebPDecBuffer mBuffer;
|
WebPDecBuffer mBuffer;
|
||||||
|
|
@ -78,11 +72,35 @@ private:
|
||||||
/// Surface format for the current frame.
|
/// Surface format for the current frame.
|
||||||
gfx::SurfaceFormat mFormat;
|
gfx::SurfaceFormat mFormat;
|
||||||
|
|
||||||
|
/// Frame rect for the current frame.
|
||||||
|
IntRect mFrameRect;
|
||||||
|
|
||||||
/// The last row of decoded pixels written to mPipe.
|
/// The last row of decoded pixels written to mPipe.
|
||||||
int mLastRow;
|
int mLastRow;
|
||||||
|
|
||||||
/// Number of decoded frames.
|
/// Number of decoded frames.
|
||||||
uint32_t mCurrentFrame;
|
uint32_t mCurrentFrame;
|
||||||
|
|
||||||
|
/// Pointer to the start of the contiguous encoded image data.
|
||||||
|
const uint8_t* mData;
|
||||||
|
|
||||||
|
/// Length of data pointed to by mData.
|
||||||
|
size_t mLength;
|
||||||
|
|
||||||
|
/// True if the iterator has reached its end.
|
||||||
|
bool mIteratorComplete;
|
||||||
|
|
||||||
|
/// True if this decoding pass requires a WebPDemuxer.
|
||||||
|
bool mNeedDemuxer;
|
||||||
|
|
||||||
|
/// True if we have setup the color profile for the image.
|
||||||
|
bool mGotColorProfile;
|
||||||
|
|
||||||
|
/// Color management profile from the ICCP chunk in the image.
|
||||||
|
qcms_profile* mInProfile;
|
||||||
|
|
||||||
|
/// Color management transform to apply to image data.
|
||||||
|
qcms_transform* mTransform;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace image
|
} // namespace image
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue