From daf859d84c27e715caec0e2fa4462a5d9faf5634 Mon Sep 17 00:00:00 2001 From: Job Bautista Date: Fri, 2 Dec 2022 13:28:34 +0800 Subject: Issue #2048 - Add progressive decoding for JPEG-XL. Based on Mozilla phab D122159, intended for bug 1709815. Also moved the temporary RGBA and premultiplication fixes to a macro, since progressive decoding also needs these fixes. --- image/decoders/nsJXLDecoder.cpp | 97 ++++++++++++++++++++++++++++------------- image/decoders/nsJXLDecoder.h | 1 + 2 files changed, 68 insertions(+), 30 deletions(-) (limited to 'image/decoders') diff --git a/image/decoders/nsJXLDecoder.cpp b/image/decoders/nsJXLDecoder.cpp index b001a46997..85276f409a 100644 --- a/image/decoders/nsJXLDecoder.cpp +++ b/image/decoders/nsJXLDecoder.cpp @@ -36,6 +36,21 @@ namespace image { } \ } while (0); +// FIXME: Quick and dirty BGRA to RGBA conversion. +// We currently have a channel ordering mis-match here. +#define JXL_RGBA_FIX \ +for (uint8_t* pixPtr = rowPtr; pixPtr < rowPtr + mInfo.xsize * 4; pixPtr+=4){ \ + std::swap(pixPtr[0], pixPtr[2]); + +// FIXME: Pre-multiply, too +#define JXL_PREMULTIPLY_FIX \ + if (pixPtr[3] < 255) { \ + pixPtr[0]=((uint16_t)pixPtr[0]*(uint16_t)pixPtr[3]) >> 8; \ + pixPtr[1]=((uint16_t)pixPtr[1]*(uint16_t)pixPtr[3]) >> 8; \ + pixPtr[2]=((uint16_t)pixPtr[2]*(uint16_t)pixPtr[3]) >> 8; \ + } \ +} + static LazyLogModule sJXLLog("JXLDecoder"); nsJXLDecoder::nsJXLDecoder(RasterImage* aImage) @@ -118,6 +133,28 @@ nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength) size_t remaining = JxlDecoderReleaseInput(mDecoder.get()); mBuffer.clear(); JXL_TRY_BOOL(mBuffer.append(aData + aLength - remaining, remaining)); + + if (mNumFrames == 0 && InFrame()) { + // If an image was flushed by JxlDecoderFlushImage, then we know that + // JXL_DEC_FRAME has already been run and there is a pipe. + if (JxlDecoderFlushImage(mDecoder.get()) == JXL_DEC_SUCCESS) { + // A full frame partial image is written to the buffer. + mPipe.ResetToFirstRow(); + for (uint8_t* rowPtr = mOutBuffer.begin(); + rowPtr < mOutBuffer.end(); rowPtr += mInfo.xsize * 4) { + JXL_RGBA_FIX JXL_PREMULTIPLY_FIX; + uint8_t* rowToWrite = rowPtr; + mPipe.WriteBuffer(reinterpret_cast(rowToWrite)); + } + + if (Maybe invalidRect = + mPipe.TakeInvalidRect()) { + PostInvalidation(invalidRect->mInputSpaceRect, + Some(invalidRect->mOutputSpaceRect)); + } + } + } + return Transition::ContinueUnbuffered(State::JXL_DATA); } @@ -158,22 +195,6 @@ nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength) return Transition::TerminateSuccess(); } - break; - } - - case JXL_DEC_NEED_IMAGE_OUT_BUFFER: { - size_t size = 0; - JxlPixelFormat format{4, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0}; - JXL_TRY(JxlDecoderImageOutBufferSize(mDecoder.get(), &format, &size)); - - mOutBuffer.clear(); - JXL_TRY_BOOL(mOutBuffer.growBy(size)); - JXL_TRY(JxlDecoderSetImageOutBuffer(mDecoder.get(), &format, - mOutBuffer.begin(), size)); - break; - } - - case JXL_DEC_FULL_IMAGE: { Maybe animParams; if (!IsFirstFrameDecode()) { animParams.emplace(AnimationParams { @@ -182,9 +203,16 @@ nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength) }); } + SurfacePipeFlags pipeFlags = SurfacePipeFlags(); + + if (mNumFrames == 0) { + // The first frame may be displayed progressively. + pipeFlags |= SurfacePipeFlags::PROGRESSIVE_DISPLAY; + } + Maybe pipe = SurfacePipeFactory::CreateSurfacePipe( this, Size(), OutputSize(), FullFrame(), SurfaceFormat::B8G8R8A8, - animParams, SurfacePipeFlags()); + animParams, pipeFlags); if (!pipe) { MOZ_LOG(sJXLLog, LogLevel::Debug, @@ -192,23 +220,32 @@ nsJXLDecoder::ReadJXLData(const char* aData, size_t aLength) return Transition::TerminateFailure(); } + mPipe = std::move(*pipe); + + break; + } + + case JXL_DEC_NEED_IMAGE_OUT_BUFFER: { + size_t size = 0; + JxlPixelFormat format{4, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0}; + JXL_TRY(JxlDecoderImageOutBufferSize(mDecoder.get(), &format, &size)); + + mOutBuffer.clear(); + JXL_TRY_BOOL(mOutBuffer.growBy(size)); + JXL_TRY(JxlDecoderSetImageOutBuffer(mDecoder.get(), &format, + mOutBuffer.begin(), size)); + break; + } + + case JXL_DEC_FULL_IMAGE: { + mPipe.ResetToFirstRow(); for (uint8_t* rowPtr = mOutBuffer.begin(); rowPtr < mOutBuffer.end(); rowPtr += mInfo.xsize * 4) { - // FIXME: Quick and dirty BGRA to RGBA conversion. - // We currently have a channel ordering mis-match here. - for (uint8_t* pixPtr = rowPtr; pixPtr < rowPtr + mInfo.xsize * 4; pixPtr+=4){ - std::swap(pixPtr[0], pixPtr[2]); - // Pre-multiply, too - if (pixPtr[3] < 255) { - pixPtr[0]=((uint16_t)pixPtr[0]*(uint16_t)pixPtr[3]) >> 8; - pixPtr[1]=((uint16_t)pixPtr[1]*(uint16_t)pixPtr[3]) >> 8; - pixPtr[2]=((uint16_t)pixPtr[2]*(uint16_t)pixPtr[3]) >> 8; - } - } - pipe->WriteBuffer(reinterpret_cast(rowPtr)); + JXL_RGBA_FIX JXL_PREMULTIPLY_FIX; + mPipe.WriteBuffer(reinterpret_cast(rowPtr)); } - if (Maybe invalidRect = pipe->TakeInvalidRect()) { + if (Maybe invalidRect = mPipe.TakeInvalidRect()) { PostInvalidation(invalidRect->mInputSpaceRect, Some(invalidRect->mOutputSpaceRect)); } diff --git a/image/decoders/nsJXLDecoder.h b/image/decoders/nsJXLDecoder.h index 6badd33124..3cabcfb9b0 100644 --- a/image/decoders/nsJXLDecoder.h +++ b/image/decoders/nsJXLDecoder.h @@ -49,6 +49,7 @@ class nsJXLDecoder final : public Decoder { uint32_t mNumFrames; FrameTimeout mTimeout; gfx::SurfaceFormat mSurfaceFormat; + SurfacePipe mPipe; bool mContinue; }; -- cgit v1.2.3