1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ImageLogging.h" // Must appear first
8 #include "gfxPlatform.h"
9 #include "jxl/codestream_header.h"
10 #include "jxl/decode_cxx.h"
11 #include "jxl/types.h"
12 #include "mozilla/TelemetryHistogramEnums.h"
13 #include "mozilla/gfx/Point.h"
14 #include "nsJXLDecoder.h"
16 #include "RasterImage.h"
17 #include "SurfacePipeFactory.h"
19 using namespace mozilla::gfx
;
21 namespace mozilla::image
{
23 #define JXL_TRY(expr) \
25 JxlDecoderStatus _status = (expr); \
26 if (_status != JXL_DEC_SUCCESS) { \
27 return Transition::TerminateFailure(); \
31 #define JXL_TRY_BOOL(expr) \
33 bool succeeded = (expr); \
35 return Transition::TerminateFailure(); \
39 static LazyLogModule
sJXLLog("JXLDecoder");
41 nsJXLDecoder::nsJXLDecoder(RasterImage
* aImage
)
43 mLexer(Transition::ToUnbuffered(State::FINISHED_JXL_DATA
, State::JXL_DATA
,
45 Transition::TerminateSuccess()),
46 mDecoder(JxlDecoderMake(nullptr)),
48 JxlThreadParallelRunnerMake(nullptr, PreferredThreadCount())) {
49 JxlDecoderSubscribeEvents(mDecoder
.get(),
50 JXL_DEC_BASIC_INFO
| JXL_DEC_FULL_IMAGE
);
51 JxlDecoderSetParallelRunner(mDecoder
.get(), JxlThreadParallelRunner
,
52 mParallelRunner
.get());
54 MOZ_LOG(sJXLLog
, LogLevel::Debug
,
55 ("[this=%p] nsJXLDecoder::nsJXLDecoder", this));
58 nsJXLDecoder::~nsJXLDecoder() {
59 MOZ_LOG(sJXLLog
, LogLevel::Debug
,
60 ("[this=%p] nsJXLDecoder::~nsJXLDecoder", this));
63 size_t nsJXLDecoder::PreferredThreadCount() {
64 if (IsMetadataDecode()) {
65 return 0; // no additional worker thread
67 return JxlThreadParallelRunnerDefaultNumWorkerThreads();
70 LexerResult
nsJXLDecoder::DoDecode(SourceBufferIterator
& aIterator
,
71 IResumable
* aOnResume
) {
72 // return LexerResult(TerminalState::FAILURE);
73 MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
75 return mLexer
.Lex(aIterator
, aOnResume
,
76 [=](State aState
, const char* aData
, size_t aLength
) {
79 return ReadJXLData(aData
, aLength
);
80 case State::FINISHED_JXL_DATA
:
81 return FinishedJXLData();
83 MOZ_CRASH("Unknown State");
87 LexerTransition
<nsJXLDecoder::State
> nsJXLDecoder::ReadJXLData(
88 const char* aData
, size_t aLength
) {
89 const uint8_t* input
= (const uint8_t*)aData
;
90 size_t length
= aLength
;
91 if (mBuffer
.length() != 0) {
92 JXL_TRY_BOOL(mBuffer
.append(aData
, aLength
));
93 input
= mBuffer
.begin();
94 length
= mBuffer
.length();
96 JXL_TRY(JxlDecoderSetInput(mDecoder
.get(), input
, length
));
99 JxlDecoderStatus status
= JxlDecoderProcessInput(mDecoder
.get());
103 return Transition::TerminateFailure();
105 case JXL_DEC_NEED_MORE_INPUT
: {
106 size_t remaining
= JxlDecoderReleaseInput(mDecoder
.get());
108 JXL_TRY_BOOL(mBuffer
.append(aData
+ aLength
- remaining
, remaining
));
109 return Transition::ContinueUnbuffered(State::JXL_DATA
);
112 case JXL_DEC_BASIC_INFO
: {
113 JXL_TRY(JxlDecoderGetBasicInfo(mDecoder
.get(), &mInfo
));
114 PostSize(mInfo
.xsize
, mInfo
.ysize
);
115 if (WantsFrameCount()) {
116 PostFrameCount(/* aFrameCount */ 1);
118 if (mInfo
.alpha_bits
> 0) {
119 PostHasTransparency();
121 if (IsMetadataDecode()) {
122 return Transition::TerminateSuccess();
127 case JXL_DEC_NEED_IMAGE_OUT_BUFFER
: {
129 JxlPixelFormat format
{4, JXL_TYPE_UINT8
, JXL_LITTLE_ENDIAN
, 0};
130 JXL_TRY(JxlDecoderImageOutBufferSize(mDecoder
.get(), &format
, &size
));
133 JXL_TRY_BOOL(mOutBuffer
.growBy(size
));
134 JXL_TRY(JxlDecoderSetImageOutBuffer(mDecoder
.get(), &format
,
135 mOutBuffer
.begin(), size
));
139 case JXL_DEC_FULL_IMAGE
: {
140 OrientedIntSize
size(mInfo
.xsize
, mInfo
.ysize
);
141 Maybe
<SurfacePipe
> pipe
= SurfacePipeFactory::CreateSurfacePipe(
142 this, size
, OutputSize(), FullFrame(), SurfaceFormat::R8G8B8A8
,
143 SurfaceFormat::OS_RGBA
, Nothing(), nullptr, SurfacePipeFlags());
144 for (uint8_t* rowPtr
= mOutBuffer
.begin(); rowPtr
< mOutBuffer
.end();
145 rowPtr
+= mInfo
.xsize
* 4) {
146 pipe
->WriteBuffer(reinterpret_cast<uint32_t*>(rowPtr
));
149 if (Maybe
<SurfaceInvalidRect
> invalidRect
= pipe
->TakeInvalidRect()) {
150 PostInvalidation(invalidRect
->mInputSpaceRect
,
151 Some(invalidRect
->mOutputSpaceRect
));
155 return Transition::TerminateSuccess();
161 LexerTransition
<nsJXLDecoder::State
> nsJXLDecoder::FinishedJXLData() {
162 MOZ_ASSERT_UNREACHABLE("Read the entire address space?");
163 return Transition::TerminateFailure();
166 } // namespace mozilla::image