summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--image/DecoderFactory.cpp5
-rw-r--r--image/decoders/nsJXLDecoder.cpp95
-rw-r--r--image/decoders/nsJXLDecoder.h6
3 files changed, 94 insertions, 12 deletions
diff --git a/image/DecoderFactory.cpp b/image/DecoderFactory.cpp
index 303cd3d63b..cd6a2fc7ad 100644
--- a/image/DecoderFactory.cpp
+++ b/image/DecoderFactory.cpp
@@ -203,6 +203,11 @@ DecoderFactory::CreateAnimationDecoder(DecoderType aType,
MOZ_ASSERT(aType == DecoderType::GIF || aType == DecoderType::PNG ||
aType == DecoderType::WEBP,
+#ifdef MOZ_JXL
+ || aType == DecoderType::JXL,
+#else
+ ,
+#endif
"Calling CreateAnimationDecoder for non-animating DecoderType");
// Create an anonymous decoder. Interaction with the SurfaceCache and the
diff --git a/image/decoders/nsJXLDecoder.cpp b/image/decoders/nsJXLDecoder.cpp
index 64a192877d..b001a46997 100644
--- a/image/decoders/nsJXLDecoder.cpp
+++ b/image/decoders/nsJXLDecoder.cpp
@@ -45,9 +45,14 @@ nsJXLDecoder::nsJXLDecoder(RasterImage* aImage)
Transition::TerminateSuccess()),
mDecoder(JxlDecoderMake(nullptr)),
mParallelRunner(
- JxlThreadParallelRunnerMake(nullptr, PreferredThreadCount())) {
+ JxlThreadParallelRunnerMake(nullptr, PreferredThreadCount())),
+ mNumFrames(0),
+ mTimeout(FrameTimeout::Forever()),
+ mSurfaceFormat(SurfaceFormat::B8G8R8X8),
+ mContinue(false) {
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,
mParallelRunner.get());
@@ -88,14 +93,19 @@ nsJXLDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
LexerTransition<nsJXLDecoder::State>
nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength)
{
- const uint8_t* input = (const uint8_t*)aData;
- size_t length = aLength;
- if (mBuffer.length() != 0) {
- JXL_TRY_BOOL(mBuffer.append(aData, aLength));
- input = mBuffer.begin();
- length = mBuffer.length();
+ // Ignore data we have already read.
+ // This will only occur as a result of a yield for animation.
+ if (!mContinue) {
+ const uint8_t* input = (const uint8_t*)aData;
+ size_t length = aLength;
+ 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) {
JxlDecoderStatus status = JxlDecoderProcessInput(mDecoder.get());
@@ -117,9 +127,37 @@ nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength)
if (mInfo.alpha_bits > 0) {
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()) {
return Transition::TerminateSuccess();
}
+
break;
}
@@ -136,9 +174,24 @@ nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength)
}
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(
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();
rowPtr += mInfo.xsize * 4) {
// FIXME: Quick and dirty BGRA to RGBA conversion.
@@ -159,8 +212,26 @@ nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength)
PostInvalidation(invalidRect->mInputSpaceRect,
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);
+ }
+ [[fallthrough]]; // We are done.
+ }
+
+ case JXL_DEC_SUCCESS: {
+ PostDecodeDone(HasAnimation() ? (int32_t)mInfo.animation.num_loops - 1
+ : 0);
return Transition::TerminateSuccess();
}
}
diff --git a/image/decoders/nsJXLDecoder.h b/image/decoders/nsJXLDecoder.h
index f6eaf7e648..6badd33124 100644
--- a/image/decoders/nsJXLDecoder.h
+++ b/image/decoders/nsJXLDecoder.h
@@ -44,6 +44,12 @@ class nsJXLDecoder final : public Decoder {
Vector<uint8_t> mBuffer;
Vector<uint8_t> mOutBuffer;
JxlBasicInfo mInfo{};
+ JxlFrameHeader mFrameHeader;
+
+ uint32_t mNumFrames;
+ FrameTimeout mTimeout;
+ gfx::SurfaceFormat mSurfaceFormat;
+ bool mContinue;
};
} // namespace image