@@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
3434# include <webp/decode.h>
3535#endif
3636#if defined (LAGRANGE_ENABLE_JXL )
37+ # include <the_Foundation/map.h>
3738# include <jxl/types.h>
3839# include <jxl/decode.h>
3940# include <jxl/codestream_header.h>
@@ -140,117 +141,178 @@ static void applyImageStyle_(enum iImageStyle style, iInt2 size, uint8_t *imgDat
140141}
141142
142143#if defined (LAGRANGE_ENABLE_JXL )
143- static uint8_t * loadJxl_ (const iBlock * data , int * x , int * y ) {
144- void * runner ;
145- JxlDecoder * dec ;
146- JxlBasicInfo info ;
147- size_t bufsize ;
148- uint8_t * imgData = NULL ;
149- JxlPixelFormat format = {
144+
145+ iDeclareType (StatefulJxlDecoder )
146+
147+ struct Impl_StatefulJxlDecoder {
148+ iMapNode * parent ;
149+ iMapNode * child [2 ];
150+ int flags ;
151+ iMapKey key ;
152+
153+ JxlDecoder * decoder ;
154+ iInt2 imSize ;
155+ uint8_t * buffer ;
156+ size_t bufferSize ;
157+ void * opaqueRunner ;
158+ size_t nSeenBytes ;
159+ };
160+
161+ iDeclareTypeConstructionArgs (StatefulJxlDecoder , iGmLinkId linkId , int wantedEvents )
162+
163+ void init_StatefulJxlDecoder (iStatefulJxlDecoder * s , iGmLinkId linkId , int wantedEvents ) {
164+ memset (s , 0 , sizeof (iStatefulJxlDecoder ));
165+
166+ s -> key = linkId ;
167+ s -> decoder = JxlDecoderCreate (NULL );
168+
169+ if (!(s -> opaqueRunner = JxlResizableParallelRunnerCreate (NULL )))
170+ fprintf (stderr , "[media] JxlResizableParallelRunnerCreate failed\n" );
171+
172+ if (s -> opaqueRunner &&
173+ JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner (s -> decoder , JxlResizableParallelRunner , s -> opaqueRunner ))
174+ fprintf (stderr , "[media] JxlDecoderSetParallelRunner failed\n" );
175+
176+ if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents (s -> decoder , wantedEvents ))
177+ fprintf (stderr , "[media] JxlDecoderSubscribeEvents failed\n" );
178+ }
179+
180+ void deinit_StatefulJxlDecoder (iStatefulJxlDecoder * s ) {
181+ JxlDecoderDestroy (s -> decoder );
182+ JxlResizableParallelRunnerDestroy (s -> opaqueRunner );
183+ free (s -> buffer );
184+ memset (s , 0 , sizeof (iStatefulJxlDecoder ));
185+ }
186+
187+ iStatefulJxlDecoder * new_StatefulJxlDecoder (iGmLinkId linkId , int wantedEvents ) {
188+ iStatefulJxlDecoder * s = iMalloc (StatefulJxlDecoder );
189+ init_StatefulJxlDecoder (s , linkId , wantedEvents );
190+ return s ;
191+ }
192+
193+ void delete_StatefulJxlDecoder (iStatefulJxlDecoder * s ) {
194+ deinit_StatefulJxlDecoder (s );
195+ free (s );
196+ }
197+
198+ static int compare_MapNode_ (iMapKey a , iMapKey b ) {
199+ return (a > b ) - (a < b );
200+ }
201+
202+ static uint8_t * loadJxl_ (const iBlock * data , iInt2 * imSize , iGmLinkId linkId , iBool isPartial ) {
203+ static iMap * decoderMap = NULL ;
204+
205+ iStatefulJxlDecoder * s ;
206+ JxlBasicInfo info ;
207+ JxlDecoderStatus status ;
208+ uint8_t * imgData = NULL ;
209+ const size_t blockSize = size_Block (data );
210+ const JxlPixelFormat format = {
150211 .num_channels = 4 ,
151212 .data_type = JXL_TYPE_UINT8 ,
152213 .endianness = JXL_NATIVE_ENDIAN ,
153214 .align = 0
154215 };
155216
156- if (!(runner = JxlResizableParallelRunnerCreate (NULL ))) {
157- fprintf (stderr , "[media] JxlResizableParallelRunnerCreate failed\n" );
158- goto ret ;
159- }
160- if (!(dec = JxlDecoderCreate (NULL ))) {
161- fprintf (stderr , "[media] JxlDecoderCreate failed\n" );
162- goto ret ;
163- }
217+ if (!decoderMap && !(decoderMap = new_Map (compare_MapNode_ )))
218+ fprintf (stderr , "[media] Creating JxlDecoderMap failed\n" );
164219
165- if (JXL_DEC_SUCCESS !=
166- JxlDecoderSubscribeEvents (dec , JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE )) {
167- fprintf (stderr , "[media] JxlDecoderSubscribeEvents failed\n" );
168- goto ret ;
169- }
170-
171- if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner (dec , JxlResizableParallelRunner , runner )) {
172- fprintf (stderr , "JxlDecoderSetParallelRunner failed\n" );
173- goto ret ;
220+
221+ if (decoderMap && contains_Map (decoderMap , linkId ))
222+ s = (iStatefulJxlDecoder * )value_Map (decoderMap , linkId );
223+ else {
224+ s = new_StatefulJxlDecoder (linkId , JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE );
225+ if (decoderMap && isPartial )
226+ insert_Map (decoderMap , (iMapNode * )s );
174227 }
175228
176- JxlDecoderSetInput (dec , constData_Block (data ), size_Block (data ));
177- JxlDecoderCloseInput (dec );
229+ JxlDecoderSetInput (s -> decoder , constData_Block (data ) + s -> nSeenBytes , blockSize - s -> nSeenBytes );
230+ if (!isPartial )
231+ JxlDecoderCloseInput (s -> decoder );
178232
179233 while (true) {
180- switch (JxlDecoderProcessInput (dec )) {
181- case JXL_DEC_ERROR :
182- fprintf (stderr , "[media] JXL decoder error\n" );
183- goto ret ;
184- case JXL_DEC_NEED_MORE_INPUT :
185- fprintf (stderr , "[media] Incomplete JXL data\n" );
186- goto ret ;
234+ switch (status = JxlDecoderProcessInput (s -> decoder )) {
187235 case JXL_DEC_BASIC_INFO :
188- if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo (dec , & info )) {
236+ if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo (s -> decoder , & info )) {
189237 fprintf (stderr , "[media] JxlDecoderGetBasicInfo failed\n" );
190- goto ret ;
238+ goto err ;
191239 }
192- * x = info .xsize ;
193- * y = info .ysize ;
240+ s -> imSize = init_I2 (info .xsize , info .ysize );
194241 JxlResizableParallelRunnerSetThreads (
195- runner , JxlResizableParallelRunnerSuggestThreads (* x , * y ));
242+ s -> opaqueRunner , JxlResizableParallelRunnerSuggestThreads (info . xsize , info . ysize ));
196243 break ;
197- case JXL_DEC_COLOR_ENCODING :
198- break ; // don't care
199244 case JXL_DEC_NEED_IMAGE_OUT_BUFFER :
200- if (JXL_DEC_SUCCESS != JxlDecoderImageOutBufferSize (dec , & format , & bufsize )) {
245+ if (JXL_DEC_SUCCESS != JxlDecoderImageOutBufferSize (s -> decoder , & format , & s -> bufferSize )) {
201246 fprintf (stderr , "JxlDecoderImageOutBufferSize failed\n" );
202- goto ret ;
203- }
204- if (bufsize != * x * * y * 4 * sizeof (uint8_t )) {
205- fprintf (stderr ,
206- "Invalid out buffer size %lu %d\n" ,
207- bufsize ,
208- * x * * y * 4 * sizeof (uint8_t ));
209- goto ret ;
247+ goto err ;
210248 }
211- imgData = realloc (imgData , bufsize );
249+ s -> buffer = realloc (s -> buffer , s -> bufferSize );
212250 if (JXL_DEC_SUCCESS !=
213- JxlDecoderSetImageOutBuffer (dec , & format , imgData , bufsize )) {
251+ JxlDecoderSetImageOutBuffer (s -> decoder , & format , s -> buffer , s -> bufferSize )) {
214252 fprintf (stderr , "JxlDecoderSetImageOutBuffer failed\n" );
215- goto ret ;
253+ goto err ;
216254 }
217255 break ;
256+ case JXL_DEC_NEED_MORE_INPUT :
257+ if (!isPartial ) {
258+ fprintf (stderr , "[media] Incomplete JXL data\n" );
259+ goto err ;
260+ }
218261 case JXL_DEC_FULL_IMAGE :
219- break ; // don't care, keep last image
262+ s -> nSeenBytes = blockSize - JxlDecoderReleaseInput (s -> decoder );
263+
264+ if (status != JXL_DEC_NEED_MORE_INPUT ||
265+ JXL_DEC_SUCCESS == JxlDecoderFlushImage (s -> decoder )) {
266+ printf ("[media] flushed jxl after %lu bytes\n" , s -> nSeenBytes );
267+ * imSize = s -> imSize ;
268+ imgData = malloc (s -> bufferSize );
269+ memcpy (imgData , s -> buffer , s -> bufferSize );
270+ }
271+
272+ goto ret ;
220273 case JXL_DEC_SUCCESS :
274+ * imSize = s -> imSize ;
275+ imgData = s -> buffer ;
276+ s -> buffer = NULL ;
221277 goto ret ;
278+ case JXL_DEC_ERROR :
279+ fprintf (stderr , "[media] JXL decoder error\n" );
280+ goto err ;
222281 default :
223282 fprintf (stderr , "[media] JXL unknown decoder status\n" );
224- goto ret ;
283+ goto err ;
225284 } /* switch (status) */
226285 } /* while */
227286
228287ret :
229- if (dec ) {
230- JxlDecoderReleaseInput (dec );
231- JxlDecoderDestroy (dec );
288+ if (!isPartial ) {
289+ err :
290+ if (decoderMap )
291+ remove_Map (decoderMap , s -> key );
292+ delete_StatefulJxlDecoder (s );
232293 }
233- if (runner ) JxlResizableParallelRunnerDestroy (runner );
234294
235295 return imgData ;
236296}
237- #endif
238297
239- void makeTexture_GmImage (iGmImage * d ) {
298+ #endif /* defined (LAGRANGE_ENABLE_JXL) */
299+
300+ iBool makeTexture_GmImage (iGmImage * d , iBool isPartial ) {
240301 iBlock * data = & d -> partialData ;
241302 d -> numBytes = size_Block (data );
242303 uint8_t * imgData = NULL ;
243- if (cmp_String (& d -> props .mime , "image/webp" ) == 0 ) {
304+ iBool isNew = iFalse ;
305+ if (!isPartial && cmp_String (& d -> props .mime , "image/webp" ) == 0 ) {
244306#if defined (LAGRANGE_ENABLE_WEBP )
245307 imgData = WebPDecodeRGBA (constData_Block (data ), size_Block (data ), & d -> size .x , & d -> size .y );
246308#endif
247309 }
248310 else if (cmp_String (& d -> props .mime , "image/jxl" ) == 0 ) {
249311#if defined (LAGRANGE_ENABLE_JXL )
250- imgData = loadJxl_ (data , & d -> size . x , & d -> size . y );
312+ imgData = loadJxl_ (data , & d -> size , d -> props . linkId , isPartial );
251313#endif
252314 }
253- else {
315+ else if (! isPartial ) {
254316 imgData = stbi_load_from_memory (
255317 constData_Block (data ), (int ) size_Block (data ), & d -> size .x , & d -> size .y , NULL , 4 );
256318 if (!imgData ) {
@@ -303,8 +365,12 @@ void makeTexture_GmImage(iGmImage *d) {
303365 d -> texture = SDL_CreateTextureFromSurface (renderer_Window (window ), surface );
304366 SDL_FreeSurface (surface );
305367 free (imgData );
368+ isNew = iTrue ;
306369 }
307- clear_Block (data );
370+ if (!isPartial )
371+ clear_Block (data );
372+
373+ return isNew ;
308374}
309375
310376iDefineTypeConstructionArgs (GmImage , (const iBlock * data ), data )
@@ -506,9 +572,7 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
506572 img = at_PtrArray (& d -> items [image_MediaType ], existingIndex );
507573 iAssert (equal_String (& img -> props .mime , mime )); /* MIME cannot change */
508574 set_Block (& img -> partialData , data );
509- if (!isPartial ) {
510- makeTexture_GmImage (img );
511- }
575+ isNew = makeTexture_GmImage (img , isPartial );
512576 }
513577 }
514578 else if (existing .type == audio_MediaType ) {
@@ -560,10 +624,7 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
560624 img -> props .isPermanent = !allowHide ;
561625 set_String (& img -> props .mime , mime );
562626 pushBack_PtrArray (& d -> items [image_MediaType ], img );
563- if (!isPartial ) {
564- makeTexture_GmImage (img );
565- }
566- isNew = iTrue ;
627+ isNew = makeTexture_GmImage (img , isPartial );
567628 }
568629 else if (startsWith_String (mime , "audio/" )) {
569630#if defined (LAGRANGE_ENABLE_AUDIO )
0 commit comments