Bug 1874684 - Part 31: Correctly reject invalid durations in some RoundDuration calls...
[gecko.git] / image / decoders / nsJXLDecoder.cpp
blobb3610f907539d66be3821cb0e8363cccc961611b
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) \
24 do { \
25 JxlDecoderStatus _status = (expr); \
26 if (_status != JXL_DEC_SUCCESS) { \
27 return Transition::TerminateFailure(); \
28 } \
29 } while (0);
31 #define JXL_TRY_BOOL(expr) \
32 do { \
33 bool succeeded = (expr); \
34 if (!succeeded) { \
35 return Transition::TerminateFailure(); \
36 } \
37 } while (0);
39 static LazyLogModule sJXLLog("JXLDecoder");
41 nsJXLDecoder::nsJXLDecoder(RasterImage* aImage)
42 : Decoder(aImage),
43 mLexer(Transition::ToUnbuffered(State::FINISHED_JXL_DATA, State::JXL_DATA,
44 SIZE_MAX),
45 Transition::TerminateSuccess()),
46 mDecoder(JxlDecoderMake(nullptr)),
47 mParallelRunner(
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) {
77 switch (aState) {
78 case State::JXL_DATA:
79 return ReadJXLData(aData, aLength);
80 case State::FINISHED_JXL_DATA:
81 return FinishedJXLData();
83 MOZ_CRASH("Unknown State");
84 });
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));
98 while (true) {
99 JxlDecoderStatus status = JxlDecoderProcessInput(mDecoder.get());
100 switch (status) {
101 case JXL_DEC_ERROR:
102 default:
103 return Transition::TerminateFailure();
105 case JXL_DEC_NEED_MORE_INPUT: {
106 size_t remaining = JxlDecoderReleaseInput(mDecoder.get());
107 mBuffer.clear();
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 (mInfo.alpha_bits > 0) {
116 PostHasTransparency();
118 if (IsMetadataDecode()) {
119 return Transition::TerminateSuccess();
121 break;
124 case JXL_DEC_NEED_IMAGE_OUT_BUFFER: {
125 size_t size = 0;
126 JxlPixelFormat format{4, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0};
127 JXL_TRY(JxlDecoderImageOutBufferSize(mDecoder.get(), &format, &size));
129 mOutBuffer.clear();
130 JXL_TRY_BOOL(mOutBuffer.growBy(size));
131 JXL_TRY(JxlDecoderSetImageOutBuffer(mDecoder.get(), &format,
132 mOutBuffer.begin(), size));
133 break;
136 case JXL_DEC_FULL_IMAGE: {
137 OrientedIntSize size(mInfo.xsize, mInfo.ysize);
138 Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(
139 this, size, OutputSize(), FullFrame(), SurfaceFormat::R8G8B8A8,
140 SurfaceFormat::OS_RGBA, Nothing(), nullptr, SurfacePipeFlags());
141 for (uint8_t* rowPtr = mOutBuffer.begin(); rowPtr < mOutBuffer.end();
142 rowPtr += mInfo.xsize * 4) {
143 pipe->WriteBuffer(reinterpret_cast<uint32_t*>(rowPtr));
146 if (Maybe<SurfaceInvalidRect> invalidRect = pipe->TakeInvalidRect()) {
147 PostInvalidation(invalidRect->mInputSpaceRect,
148 Some(invalidRect->mOutputSpaceRect));
150 PostFrameStop();
151 PostDecodeDone();
152 return Transition::TerminateSuccess();
158 LexerTransition<nsJXLDecoder::State> nsJXLDecoder::FinishedJXLData() {
159 MOZ_ASSERT_UNREACHABLE("Read the entire address space?");
160 return Transition::TerminateFailure();
163 } // namespace mozilla::image