nsJXLDecoder.cpp (5869B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 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/. */ 6 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" 15 16 #include "RasterImage.h" 17 #include "SurfacePipeFactory.h" 18 19 using namespace mozilla::gfx; 20 21 namespace mozilla::image { 22 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); 30 31 #define JXL_TRY_BOOL(expr) \ 32 do { \ 33 bool succeeded = (expr); \ 34 if (!succeeded) { \ 35 return Transition::TerminateFailure(); \ 36 } \ 37 } while (0); 38 39 static LazyLogModule sJXLLog("JXLDecoder"); 40 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()); 53 54 MOZ_LOG(sJXLLog, LogLevel::Debug, 55 ("[this=%p] nsJXLDecoder::nsJXLDecoder", this)); 56 } 57 58 nsJXLDecoder::~nsJXLDecoder() { 59 MOZ_LOG(sJXLLog, LogLevel::Debug, 60 ("[this=%p] nsJXLDecoder::~nsJXLDecoder", this)); 61 } 62 63 size_t nsJXLDecoder::PreferredThreadCount() { 64 if (IsMetadataDecode()) { 65 return 0; // no additional worker thread 66 } 67 return JxlThreadParallelRunnerDefaultNumWorkerThreads(); 68 } 69 70 LexerResult nsJXLDecoder::DoDecode(SourceBufferIterator& aIterator, 71 IResumable* aOnResume) { 72 // return LexerResult(TerminalState::FAILURE); 73 MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!"); 74 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(); 82 } 83 MOZ_CRASH("Unknown State"); 84 }); 85 }; 86 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(); 95 } 96 JXL_TRY(JxlDecoderSetInput(mDecoder.get(), input, length)); 97 98 while (true) { 99 JxlDecoderStatus status = JxlDecoderProcessInput(mDecoder.get()); 100 switch (status) { 101 case JXL_DEC_ERROR: 102 default: 103 return Transition::TerminateFailure(); 104 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); 110 } 111 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); 117 } 118 if (mInfo.alpha_bits > 0) { 119 PostHasTransparency(); 120 } 121 if (IsMetadataDecode()) { 122 return Transition::TerminateSuccess(); 123 } 124 break; 125 } 126 127 case JXL_DEC_NEED_IMAGE_OUT_BUFFER: { 128 size_t size = 0; 129 JxlPixelFormat format{4, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0}; 130 JXL_TRY(JxlDecoderImageOutBufferSize(mDecoder.get(), &format, &size)); 131 132 mOutBuffer.clear(); 133 JXL_TRY_BOOL(mOutBuffer.growBy(size)); 134 JXL_TRY(JxlDecoderSetImageOutBuffer(mDecoder.get(), &format, 135 mOutBuffer.begin(), size)); 136 break; 137 } 138 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)); 147 } 148 149 if (Maybe<SurfaceInvalidRect> invalidRect = pipe->TakeInvalidRect()) { 150 PostInvalidation(invalidRect->mInputSpaceRect, 151 Some(invalidRect->mOutputSpaceRect)); 152 } 153 PostFrameStop(); 154 PostDecodeDone(); 155 return Transition::TerminateSuccess(); 156 } 157 } 158 } 159 } 160 161 LexerTransition<nsJXLDecoder::State> nsJXLDecoder::FinishedJXLData() { 162 MOZ_ASSERT_UNREACHABLE("Read the entire address space?"); 163 return Transition::TerminateFailure(); 164 } 165 166 } // namespace mozilla::image