@@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
34
34
# include <webp/decode.h>
35
35
#endif
36
36
#if defined (LAGRANGE_ENABLE_JXL )
37
+ # include <the_Foundation/map.h>
37
38
# include <jxl/types.h>
38
39
# include <jxl/decode.h>
39
40
# include <jxl/codestream_header.h>
@@ -140,117 +141,178 @@ static void applyImageStyle_(enum iImageStyle style, iInt2 size, uint8_t *imgDat
140
141
}
141
142
142
143
#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 = {
150
211
.num_channels = 4 ,
151
212
.data_type = JXL_TYPE_UINT8 ,
152
213
.endianness = JXL_NATIVE_ENDIAN ,
153
214
.align = 0
154
215
};
155
216
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" );
164
219
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 );
174
227
}
175
228
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 );
178
232
179
233
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 )) {
187
235
case JXL_DEC_BASIC_INFO :
188
- if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo (dec , & info )) {
236
+ if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo (s -> decoder , & info )) {
189
237
fprintf (stderr , "[media] JxlDecoderGetBasicInfo failed\n" );
190
- goto ret ;
238
+ goto err ;
191
239
}
192
- * x = info .xsize ;
193
- * y = info .ysize ;
240
+ s -> imSize = init_I2 (info .xsize , info .ysize );
194
241
JxlResizableParallelRunnerSetThreads (
195
- runner , JxlResizableParallelRunnerSuggestThreads (* x , * y ));
242
+ s -> opaqueRunner , JxlResizableParallelRunnerSuggestThreads (info . xsize , info . ysize ));
196
243
break ;
197
- case JXL_DEC_COLOR_ENCODING :
198
- break ; // don't care
199
244
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 )) {
201
246
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 ;
210
248
}
211
- imgData = realloc (imgData , bufsize );
249
+ s -> buffer = realloc (s -> buffer , s -> bufferSize );
212
250
if (JXL_DEC_SUCCESS !=
213
- JxlDecoderSetImageOutBuffer (dec , & format , imgData , bufsize )) {
251
+ JxlDecoderSetImageOutBuffer (s -> decoder , & format , s -> buffer , s -> bufferSize )) {
214
252
fprintf (stderr , "JxlDecoderSetImageOutBuffer failed\n" );
215
- goto ret ;
253
+ goto err ;
216
254
}
217
255
break ;
256
+ case JXL_DEC_NEED_MORE_INPUT :
257
+ if (!isPartial ) {
258
+ fprintf (stderr , "[media] Incomplete JXL data\n" );
259
+ goto err ;
260
+ }
218
261
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 ;
220
273
case JXL_DEC_SUCCESS :
274
+ * imSize = s -> imSize ;
275
+ imgData = s -> buffer ;
276
+ s -> buffer = NULL ;
221
277
goto ret ;
278
+ case JXL_DEC_ERROR :
279
+ fprintf (stderr , "[media] JXL decoder error\n" );
280
+ goto err ;
222
281
default :
223
282
fprintf (stderr , "[media] JXL unknown decoder status\n" );
224
- goto ret ;
283
+ goto err ;
225
284
} /* switch (status) */
226
285
} /* while */
227
286
228
287
ret :
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 );
232
293
}
233
- if (runner ) JxlResizableParallelRunnerDestroy (runner );
234
294
235
295
return imgData ;
236
296
}
237
- #endif
238
297
239
- void makeTexture_GmImage (iGmImage * d ) {
298
+ #endif /* defined (LAGRANGE_ENABLE_JXL) */
299
+
300
+ iBool makeTexture_GmImage (iGmImage * d , iBool isPartial ) {
240
301
iBlock * data = & d -> partialData ;
241
302
d -> numBytes = size_Block (data );
242
303
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 ) {
244
306
#if defined (LAGRANGE_ENABLE_WEBP )
245
307
imgData = WebPDecodeRGBA (constData_Block (data ), size_Block (data ), & d -> size .x , & d -> size .y );
246
308
#endif
247
309
}
248
310
else if (cmp_String (& d -> props .mime , "image/jxl" ) == 0 ) {
249
311
#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 );
251
313
#endif
252
314
}
253
- else {
315
+ else if (! isPartial ) {
254
316
imgData = stbi_load_from_memory (
255
317
constData_Block (data ), (int ) size_Block (data ), & d -> size .x , & d -> size .y , NULL , 4 );
256
318
if (!imgData ) {
@@ -303,8 +365,12 @@ void makeTexture_GmImage(iGmImage *d) {
303
365
d -> texture = SDL_CreateTextureFromSurface (renderer_Window (window ), surface );
304
366
SDL_FreeSurface (surface );
305
367
free (imgData );
368
+ isNew = iTrue ;
306
369
}
307
- clear_Block (data );
370
+ if (!isPartial )
371
+ clear_Block (data );
372
+
373
+ return isNew ;
308
374
}
309
375
310
376
iDefineTypeConstructionArgs (GmImage , (const iBlock * data ), data )
@@ -506,9 +572,7 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
506
572
img = at_PtrArray (& d -> items [image_MediaType ], existingIndex );
507
573
iAssert (equal_String (& img -> props .mime , mime )); /* MIME cannot change */
508
574
set_Block (& img -> partialData , data );
509
- if (!isPartial ) {
510
- makeTexture_GmImage (img );
511
- }
575
+ isNew = makeTexture_GmImage (img , isPartial );
512
576
}
513
577
}
514
578
else if (existing .type == audio_MediaType ) {
@@ -560,10 +624,7 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
560
624
img -> props .isPermanent = !allowHide ;
561
625
set_String (& img -> props .mime , mime );
562
626
pushBack_PtrArray (& d -> items [image_MediaType ], img );
563
- if (!isPartial ) {
564
- makeTexture_GmImage (img );
565
- }
566
- isNew = iTrue ;
627
+ isNew = makeTexture_GmImage (img , isPartial );
567
628
}
568
629
else if (startsWith_String (mime , "audio/" )) {
569
630
#if defined (LAGRANGE_ENABLE_AUDIO )
0 commit comments