Issue #2041 - Add animation support for JPEG-XL.

Based on Mozilla phab D122158, intended for bug 1709818.
This commit is contained in:
Job Bautista 2022-12-02 11:15:05 +08:00 • committed by roytam1
commit 042b8f37ae
3 changed files with 94 additions and 12 deletions

View file

@ -204,6 +204,11 @@ DecoderFactory::CreateAnimationDecoder(DecoderType aType,
MOZ_ASSERT(aType == DecoderType::GIF || aType == DecoderType::PNG || MOZ_ASSERT(aType == DecoderType::GIF || aType == DecoderType::PNG ||
aType == DecoderType::WEBP, aType == DecoderType::WEBP,
#ifdef MOZ_JXL
|| aType == DecoderType::JXL,
#else
,
#endif
"Calling CreateAnimationDecoder for non-animating DecoderType"); "Calling CreateAnimationDecoder for non-animating DecoderType");
// Create an anonymous decoder. Interaction with the SurfaceCache and the // Create an anonymous decoder. Interaction with the SurfaceCache and the

View file

@ -45,9 +45,14 @@ nsJXLDecoder::nsJXLDecoder(RasterImage* aImage)
Transition::TerminateSuccess()), Transition::TerminateSuccess()),
mDecoder(JxlDecoderMake(nullptr)), mDecoder(JxlDecoderMake(nullptr)),
mParallelRunner( mParallelRunner(
JxlThreadParallelRunnerMake(nullptr, PreferredThreadCount())) { JxlThreadParallelRunnerMake(nullptr, PreferredThreadCount())),
mNumFrames(0),
mTimeout(FrameTimeout::Forever()),
mSurfaceFormat(SurfaceFormat::B8G8R8X8),
mContinue(false) {
JxlDecoderSubscribeEvents(mDecoder.get(), JxlDecoderSubscribeEvents(mDecoder.get(),
JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE); JXL_DEC_BASIC_INFO | JXL_DEC_FRAME |
JXL_DEC_FULL_IMAGE);
JxlDecoderSetParallelRunner(mDecoder.get(), JxlThreadParallelRunner, JxlDecoderSetParallelRunner(mDecoder.get(), JxlThreadParallelRunner,
mParallelRunner.get()); mParallelRunner.get());
@ -88,14 +93,19 @@ nsJXLDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
LexerTransition<nsJXLDecoder::State> LexerTransition<nsJXLDecoder::State>
nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength) nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength)
{ {
const uint8_t* input = (const uint8_t*)aData; // Ignore data we have already read.
size_t length = aLength; // This will only occur as a result of a yield for animation.
if (mBuffer.length() != 0) { if (!mContinue) {
JXL_TRY_BOOL(mBuffer.append(aData, aLength)); const uint8_t* input = (const uint8_t*)aData;
input = mBuffer.begin(); size_t length = aLength;
length = mBuffer.length(); if (mBuffer.length() != 0) {
JXL_TRY_BOOL(mBuffer.append(aData, aLength));
input = mBuffer.begin();
length = mBuffer.length();
}
JXL_TRY(JxlDecoderSetInput(mDecoder.get(), input, length));
} }
JXL_TRY(JxlDecoderSetInput(mDecoder.get(), input, length)); mContinue = false;
while (true) { while (true) {
JxlDecoderStatus status = JxlDecoderProcessInput(mDecoder.get()); JxlDecoderStatus status = JxlDecoderProcessInput(mDecoder.get());
@ -117,9 +127,37 @@ nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength)
if (mInfo.alpha_bits > 0) { if (mInfo.alpha_bits > 0) {
PostHasTransparency(); PostHasTransparency();
} }
if (!mInfo.have_animation && IsMetadataDecode()) {
return Transition::TerminateSuccess();
}
break;
}
case JXL_DEC_FRAME: {
if (mInfo.have_animation) {
JXL_TRY(JxlDecoderGetFrameHeader(mDecoder.get(), &mFrameHeader));
int32_t duration = (int32_t)(1000.0 * mFrameHeader.duration *
mInfo.animation.tps_denominator /
mInfo.animation.tps_numerator);
mTimeout = FrameTimeout::FromRawMilliseconds(duration);
if (!HasAnimation()) {
PostIsAnimated(mTimeout);
}
}
bool is_last = mInfo.have_animation ? mFrameHeader.is_last : true;
MOZ_LOG(sJXLLog, LogLevel::Debug,
("[this=%p] nsJXLDecoder::ReadJXLData - frame %d, is_last %d, "
"metadata decode %d, first frame decode %d\n",
this, mNumFrames, is_last, IsMetadataDecode(),
IsFirstFrameDecode()));
if (IsMetadataDecode()) { if (IsMetadataDecode()) {
return Transition::TerminateSuccess(); return Transition::TerminateSuccess();
} }
break; break;
} }
@ -136,9 +174,24 @@ nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength)
} }
case JXL_DEC_FULL_IMAGE: { case JXL_DEC_FULL_IMAGE: {
Maybe<AnimationParams> animParams;
if (!IsFirstFrameDecode()) {
animParams.emplace(AnimationParams {
FullFrame().ToUnknownRect(), mTimeout, mNumFrames,
BlendMethod::SOURCE, DisposalMethod::CLEAR
});
}
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe( Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
this, Size(), OutputSize(), FullFrame(), SurfaceFormat::B8G8R8A8, this, Size(), OutputSize(), FullFrame(), SurfaceFormat::B8G8R8A8,
Nothing(), SurfacePipeFlags()); animParams, SurfacePipeFlags());
if (!pipe) {
MOZ_LOG(sJXLLog, LogLevel::Debug,
("[this=%p] nsJXLDecoder::ReadJXLData - no pipe\n", this));
return Transition::TerminateFailure();
}
for (uint8_t* rowPtr = mOutBuffer.begin(); rowPtr < mOutBuffer.end(); for (uint8_t* rowPtr = mOutBuffer.begin(); rowPtr < mOutBuffer.end();
rowPtr += mInfo.xsize * 4) { rowPtr += mInfo.xsize * 4) {
// FIXME: Quick and dirty BGRA to RGBA conversion. // FIXME: Quick and dirty BGRA to RGBA conversion.
@ -159,8 +212,26 @@ nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength)
PostInvalidation(invalidRect->mInputSpaceRect, PostInvalidation(invalidRect->mInputSpaceRect,
Some(invalidRect->mOutputSpaceRect)); Some(invalidRect->mOutputSpaceRect));
} }
PostFrameStop();
PostDecodeDone(); Opacity opacity = mSurfaceFormat == SurfaceFormat::B8G8R8A8
? Opacity::SOME_TRANSPARENCY
: Opacity::FULLY_OPAQUE;
PostFrameStop(opacity);
if (!IsFirstFrameDecode() && mInfo.have_animation &&
!mFrameHeader.is_last) {
mNumFrames++;
mContinue = true;
// Notify for a new frame but there may be data in the current buffer
// that can immediately be processed.
return Transition::ToAfterYield(State::JXL_DATA);
}
MOZ_FALLTHROUGH; // We are done.
}
case JXL_DEC_SUCCESS: {
PostDecodeDone(HasAnimation() ? (int32_t)mInfo.animation.num_loops - 1
: 0);
return Transition::TerminateSuccess(); return Transition::TerminateSuccess();
} }
} }

View file

@ -44,6 +44,12 @@ class nsJXLDecoder final : public Decoder {
Vector<uint8_t> mBuffer; Vector<uint8_t> mBuffer;
Vector<uint8_t> mOutBuffer; Vector<uint8_t> mOutBuffer;
JxlBasicInfo mInfo{}; JxlBasicInfo mInfo{};
JxlFrameHeader mFrameHeader;
uint32_t mNumFrames;
FrameTimeout mTimeout;
gfx::SurfaceFormat mSurfaceFormat;
bool mContinue;
}; };
} // namespace image } // namespace image