88
99#include " nsJXLDecoder.h"
1010
11+ #include " mozilla/CheckedInt.h"
1112#include " RasterImage.h"
13+ #include " SurfacePipeFactory.h"
14+ #include " mozilla/Vector.h"
15+
16+ using namespace mozilla ::gfx;
1217
1318namespace mozilla ::image {
1419
@@ -23,6 +28,11 @@ nsJXLDecoder::nsJXLDecoder(RasterImage* aImage)
2328 (" [this=%p] nsJXLDecoder::nsJXLDecoder" , this ));
2429}
2530
31+ nsresult nsJXLDecoder::InitInternal () {
32+ mDecoder .reset (jxl_decoder_new (IsMetadataDecode ()));
33+ return NS_OK;
34+ }
35+
2636nsJXLDecoder::~nsJXLDecoder () {
2737 MOZ_LOG (sJXLLog , LogLevel::Debug,
2838 (" [this=%p] nsJXLDecoder::~nsJXLDecoder" , this ));
@@ -46,13 +56,141 @@ LexerResult nsJXLDecoder::DoDecode(SourceBufferIterator& aIterator,
4656
4757LexerTransition<nsJXLDecoder::State> nsJXLDecoder::ReadJXLData (
4858 const char * aData, size_t aLength) {
49- // Stub: JXL decoder removed, waiting for Rust replacement
50- return Transition::TerminateFailure ();
59+ MOZ_ASSERT (mDecoder );
60+
61+ const uint8_t * currentData = reinterpret_cast <const uint8_t *>(aData);
62+ size_t currentLength = aLength;
63+
64+ while (true ) {
65+ uint8_t * bufferPtr = mPixelBuffer .empty () ? nullptr : mPixelBuffer .begin ();
66+ size_t bufferLen = mPixelBuffer .length ();
67+
68+ JxlDecoderStatus status = jxl_decoder_process_data (
69+ mDecoder .get (), ¤tData, ¤tLength, bufferPtr, bufferLen);
70+
71+ switch (status) {
72+ case JxlDecoderStatus::Ok: {
73+ if (!HasSize ()) {
74+ JxlBasicInfo basicInfo = jxl_decoder_get_basic_info (mDecoder .get ());
75+ if (!basicInfo.valid ) {
76+ if (currentLength == 0 ) {
77+ return Transition::ContinueUnbuffered (State::JXL_DATA);
78+ } else {
79+ break ;
80+ }
81+ }
82+
83+ if (basicInfo.width > INT32_MAX || basicInfo.height > INT32_MAX) {
84+ return Transition::TerminateFailure ();
85+ }
86+
87+ PostSize (basicInfo.width , basicInfo.height );
88+ if (basicInfo.has_alpha ) {
89+ PostHasTransparency ();
90+ }
91+ PostFrameCount (1 ); // Static JXL = 1 frame
92+
93+ if (IsMetadataDecode ()) {
94+ return Transition::TerminateSuccess ();
95+ }
96+ }
97+
98+ bool frameReady = jxl_decoder_is_frame_ready (mDecoder .get ());
99+
100+ // Check if frame is ready for rendering and we need to allocate buffer
101+ if (HasSize () && frameReady && mPixelBuffer .empty ()) {
102+ OrientedIntSize size = Size ();
103+ CheckedInt<size_t > bufferSize =
104+ CheckedInt<size_t >(size.width ) * size.height * 4 ;
105+ if (!bufferSize.isValid ()) {
106+ MOZ_LOG (sJXLLog , LogLevel::Error,
107+ (" [this=%p] nsJXLDecoder::ReadJXLData -- buffer size "
108+ " overflow\n " ,
109+ this ));
110+ return Transition::TerminateFailure ();
111+ }
112+ if (!mPixelBuffer .resize (bufferSize.value ())) {
113+ MOZ_LOG (sJXLLog , LogLevel::Error,
114+ (" [this=%p] nsJXLDecoder::ReadJXLData -- failed to "
115+ " allocate pixel buffer\n " ,
116+ this ));
117+ return Transition::TerminateFailure ();
118+ }
119+ break ;
120+ }
121+
122+ // Frame rendering complete when frameReady becomes false
123+ if (HasSize () && !frameReady && !mPixelBuffer .empty ()) {
124+ // The pixel buffer has been filled by jxl_decoder_process_data.
125+ // Send it through the surface pipeline and finish decoding.
126+ nsresult rv = ProcessFrame (mPixelBuffer );
127+ if (NS_FAILED(rv)) {
128+ return Transition::TerminateFailure ();
129+ }
130+
131+ PostDecodeDone ();
132+ return Transition::TerminateSuccess ();
133+ } else if (currentLength == 0 ) {
134+ return Transition::ContinueUnbuffered (State::JXL_DATA);
135+ }
136+ break ;
137+ }
138+
139+ case JxlDecoderStatus::NeedMoreData: {
140+ if (currentLength == 0 ) {
141+ return Transition::ContinueUnbuffered (State::JXL_DATA);
142+ }
143+ break ;
144+ }
145+
146+ case JxlDecoderStatus::Error:
147+ return Transition::TerminateFailure ();
148+ }
149+ }
51150}
52151
53152LexerTransition<nsJXLDecoder::State> nsJXLDecoder::FinishedJXLData () {
54153 MOZ_ASSERT_UNREACHABLE (" Read the entire address space?" );
55154 return Transition::TerminateFailure ();
56155}
57156
157+ nsresult nsJXLDecoder::ProcessFrame (Vector<uint8_t >& aPixelBuffer) {
158+ MOZ_ASSERT (HasSize ());
159+ MOZ_ASSERT (mDecoder );
160+
161+ JxlBasicInfo basicInfo = jxl_decoder_get_basic_info (mDecoder .get ());
162+
163+ OrientedIntSize size = Size ();
164+ SurfaceFormat inFormat = SurfaceFormat::R8G8B8A8;
165+ SurfaceFormat outFormat =
166+ basicInfo.has_alpha ? SurfaceFormat::OS_RGBA : SurfaceFormat::OS_RGBX;
167+ SurfacePipeFlags pipeFlags = SurfacePipeFlags ();
168+
169+ Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe (
170+ this , size, OutputSize (), FullFrame (), inFormat, outFormat, Nothing (),
171+ nullptr , pipeFlags);
172+ if (!pipe) {
173+ return NS_ERROR_FAILURE;
174+ }
175+
176+ uint8_t * currentRow = aPixelBuffer.begin ();
177+ for (int32_t y = 0 ; y < size.height ; ++y) {
178+ WriteState result =
179+ pipe->WriteBuffer (reinterpret_cast <uint32_t *>(currentRow));
180+ if (result == WriteState::FAILURE) {
181+ return NS_ERROR_FAILURE;
182+ }
183+ currentRow += size.width * 4 ;
184+ }
185+
186+ if (Maybe<SurfaceInvalidRect> invalidRect = pipe->TakeInvalidRect ()) {
187+ PostInvalidation (invalidRect->mInputSpaceRect ,
188+ Some (invalidRect->mOutputSpaceRect ));
189+ }
190+
191+ PostFrameStop (basicInfo.has_alpha ? Opacity::SOME_TRANSPARENCY
192+ : Opacity::FULLY_OPAQUE);
193+ return NS_OK;
194+ }
195+
58196} // namespace mozilla::image
0 commit comments