Dactyloidae/image/decoders/nsJXLDecoder.cpp
Job Bautista 2df558509f Issue #1769 - Part 3: Cleanup nsJXLDecoder.
The mp4parse Rust component has been removed in Issue #58. It doesn't seem
 like the decoder uses any mp4parse function, so seems like it's safe to
 remove it.
Removed const DecoderType named GetType() because we don't have it in our
 Decoder.h.
I've decided to abandon the OS_RGBA backporting effort and instead went for
 using OS_RGBA's value which is R8G8B8A8. Turns out that there is much more
 going on in Mozilla's version of CreateSurfacePipe than I've expected, like
 the aInFormat and aOutFormat thing which I really don't want to delve into
 right now. Looking at the code it looks like all JPEG-XL images are expected
 to have an alpha channel anyway, so I think my workaround should be safe.
I also removed the dependency in OrientedIntSize and used Size() from gfx/2d/
 Point.h like our PNG and GIF decoders currently do. Migrating our decoders
 to OrientedInt types should be done in another PR instead.
Also also changed the coding style to be closer to UXP's.
Also also also, I made a mistake in Part 1's commit message; the commit used
for libjxl is 192ddd90fdf0c69cd1db1c8d7850db036dd87f4b.
2023-06-24 12:16:41 +08:00

165 lines
5.5 KiB
C++

/* -*- 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
* 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/. */
#include "ImageLogging.h" // Must appear first
#include "gfxPlatform.h"
#include "jxl/codestream_header.h"
#include "jxl/decode_cxx.h"
#include "jxl/types.h"
#include "mozilla/gfx/Point.h"
#include "nsJXLDecoder.h"
#include "RasterImage.h"
#include "SurfacePipeFactory.h"
using namespace mozilla::gfx;
namespace mozilla::image {
#define JXL_TRY(expr) \
do { \
JxlDecoderStatus status = (expr); \
if (status != JXL_DEC_SUCCESS) { \
return Transition::TerminateFailure(); \
} \
} while (0);
#define JXL_TRY_BOOL(expr) \
do { \
bool succeeded = (expr); \
if (!succeeded) { \
return Transition::TerminateFailure(); \
} \
} while (0);
static LazyLogModule sJXLLog("JXLDecoder");
nsJXLDecoder::nsJXLDecoder(RasterImage* aImage)
: Decoder(aImage),
mLexer(Transition::ToUnbuffered(State::FINISHED_JXL_DATA, State::JXL_DATA,
SIZE_MAX),
Transition::TerminateSuccess()),
mDecoder(JxlDecoderMake(nullptr)),
mParallelRunner(
JxlThreadParallelRunnerMake(nullptr, PreferredThreadCount())) {
JxlDecoderSubscribeEvents(mDecoder.get(),
JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE);
JxlDecoderSetParallelRunner(mDecoder.get(), JxlThreadParallelRunner,
mParallelRunner.get());
MOZ_LOG(sJXLLog, LogLevel::Debug,
("[this=%p] nsJXLDecoder::nsJXLDecoder", this));
}
nsJXLDecoder::~nsJXLDecoder() {
MOZ_LOG(sJXLLog, LogLevel::Debug,
("[this=%p] nsJXLDecoder::~nsJXLDecoder", this));
}
size_t nsJXLDecoder::PreferredThreadCount() {
if (IsMetadataDecode()) {
return 0; // no additional worker thread
}
return JxlThreadParallelRunnerDefaultNumWorkerThreads();
}
LexerResult
nsJXLDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
{
// return LexerResult(TerminalState::FAILURE);
MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
return mLexer.Lex(aIterator, aOnResume,
[=](State aState, const char* aData, size_t aLength) {
switch (aState) {
case State::JXL_DATA:
return ReadJXLData(aData, aLength);
case State::FINISHED_JXL_DATA:
return FinishedJXLData();
}
MOZ_CRASH("Unknown State");
});
};
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();
}
JXL_TRY(JxlDecoderSetInput(mDecoder.get(), input, length));
while (true) {
JxlDecoderStatus status = JxlDecoderProcessInput(mDecoder.get());
switch (status) {
case JXL_DEC_ERROR:
default:
return Transition::TerminateFailure();
case JXL_DEC_NEED_MORE_INPUT: {
size_t remaining = JxlDecoderReleaseInput(mDecoder.get());
mBuffer.clear();
JXL_TRY_BOOL(mBuffer.append(aData + aLength - remaining, remaining));
return Transition::ContinueUnbuffered(State::JXL_DATA);
}
case JXL_DEC_BASIC_INFO: {
JXL_TRY(JxlDecoderGetBasicInfo(mDecoder.get(), &mInfo));
PostSize(mInfo.xsize, mInfo.ysize);
if (mInfo.alpha_bits > 0) {
PostHasTransparency();
}
if (IsMetadataDecode()) {
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<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
this, Size(), OutputSize(), FullFrame(), SurfaceFormat::B8G8R8A8,
Nothing(), SurfacePipeFlags());
for (uint8_t* rowPtr = mOutBuffer.begin(); rowPtr < mOutBuffer.end();
rowPtr += mInfo.xsize * 4) {
pipe->WriteBuffer(reinterpret_cast<uint32_t*>(rowPtr));
}
if (Maybe<SurfaceInvalidRect> invalidRect = pipe->TakeInvalidRect()) {
PostInvalidation(invalidRect->mInputSpaceRect,
Some(invalidRect->mOutputSpaceRect));
}
PostFrameStop();
PostDecodeDone();
return Transition::TerminateSuccess();
}
}
}
}
LexerTransition<nsJXLDecoder::State>
nsJXLDecoder::FinishedJXLData()
{
MOZ_ASSERT_UNREACHABLE("Read the entire address space?");
return Transition::TerminateFailure();
}
} // namespace mozilla::image