@@ -91,6 +91,10 @@ typedef struct _VipsForeignLoadJxl {
91
91
JxlPixelFormat format ;
92
92
size_t icc_size ;
93
93
uint8_t * icc_data ;
94
+ size_t exif_size ;
95
+ uint8_t * exif_data ;
96
+ size_t xmp_size ;
97
+ uint8_t * xmp_data ;
94
98
95
99
/* Decompress state.
96
100
*/
@@ -102,6 +106,11 @@ typedef struct _VipsForeignLoadJxl {
102
106
uint8_t input_buffer [INPUT_BUFFER_SIZE ];
103
107
size_t bytes_in_buffer ;
104
108
109
+ /* Pointers to fields where box size and box data should be written to
110
+ */
111
+ size_t * box_size ;
112
+ uint8_t * * box_data ;
113
+
105
114
/* Number of errors reported during load -- use this to block load of
106
115
* corrupted images.
107
116
*/
@@ -133,6 +142,8 @@ vips_foreign_load_jxl_dispose(GObject *gobject)
133
142
VIPS_FREEF (JxlThreadParallelRunnerDestroy , jxl -> runner );
134
143
VIPS_FREEF (JxlDecoderDestroy , jxl -> decoder );
135
144
VIPS_FREE (jxl -> icc_data );
145
+ VIPS_FREE (jxl -> exif_data );
146
+ VIPS_FREE (jxl -> xmp_data );
136
147
VIPS_UNREF (jxl -> source );
137
148
138
149
G_OBJECT_CLASS (vips_foreign_load_jxl_parent_class )-> dispose (gobject );
@@ -191,6 +202,46 @@ vips_foreign_load_jxl_get_flags(VipsForeignLoad *load)
191
202
return VIPS_FOREIGN_PARTIAL ;
192
203
}
193
204
205
+ static int
206
+ vips_foreign_load_jxl_set_box_buffer (VipsForeignLoadJxl * jxl )
207
+ {
208
+ if (!jxl -> box_data || !jxl -> box_size )
209
+ return 0 ;
210
+
211
+ VipsObjectClass * class = VIPS_OBJECT_GET_CLASS (jxl );
212
+
213
+ uint8_t * new_data ;
214
+ size_t new_size ;
215
+ size_t box_size = * jxl -> box_size ;
216
+
217
+ new_size = box_size + INPUT_BUFFER_SIZE ;
218
+ new_data = g_try_realloc (* jxl -> box_data , new_size );
219
+
220
+ if (!new_data ) {
221
+ vips_error (class -> nickname , "%s" , _ ("out of memory" ));
222
+ return -1 ;
223
+ }
224
+
225
+ * jxl -> box_data = new_data ;
226
+
227
+ JxlDecoderSetBoxBuffer (jxl -> decoder ,
228
+ new_data + box_size , INPUT_BUFFER_SIZE );
229
+
230
+ return 0 ;
231
+ }
232
+
233
+ static int
234
+ vips_foreign_load_jxl_release_box_buffer (VipsForeignLoadJxl * jxl )
235
+ {
236
+ if (!jxl -> box_data || !jxl -> box_size )
237
+ return 0 ;
238
+
239
+ size_t remaining = JxlDecoderReleaseBoxBuffer (jxl -> decoder );
240
+ * jxl -> box_size += INPUT_BUFFER_SIZE - remaining ;
241
+
242
+ return 0 ;
243
+ }
244
+
194
245
static int
195
246
vips_foreign_load_jxl_fill_input (VipsForeignLoadJxl * jxl ,
196
247
size_t bytes_remaining )
@@ -255,6 +306,10 @@ vips_foreign_load_jxl_print_status(JxlDecoderStatus status)
255
306
printf ("JXL_DEC_JPEG_NEED_MORE_OUTPUT\n" );
256
307
break ;
257
308
309
+ case JXL_DEC_BOX_NEED_MORE_OUTPUT :
310
+ printf ("JXL_DEC_BOX_NEED_MORE_OUTPUT\n" );
311
+ break ;
312
+
258
313
case JXL_DEC_BASIC_INFO :
259
314
printf ("JXL_DEC_BASIC_INFO\n" );
260
315
break ;
@@ -287,6 +342,10 @@ vips_foreign_load_jxl_print_status(JxlDecoderStatus status)
287
342
printf ("JXL_DEC_JPEG_RECONSTRUCTION\n" );
288
343
break ;
289
344
345
+ case JXL_DEC_BOX :
346
+ printf ("JXL_DEC_BOX\n" );
347
+ break ;
348
+
290
349
default :
291
350
printf ("JXL_DEC_<unknown>\n" );
292
351
break ;
@@ -390,6 +449,55 @@ vips_foreign_load_jxl_process(VipsForeignLoadJxl *jxl)
390
449
return status ;
391
450
}
392
451
452
+ /* JPEG XL stores EXIF data without leading "Exif\0\0" with offset
453
+ */
454
+ static int
455
+ vips_foreign_load_jxl_fix_exif (VipsForeignLoadJxl * jxl )
456
+ {
457
+ VipsObjectClass * class = VIPS_OBJECT_GET_CLASS (jxl );
458
+
459
+ if (!jxl -> exif_data || vips_isprefix ("Exif" , (char * ) jxl -> exif_data ))
460
+ return 0 ;
461
+
462
+ size_t old_size = jxl -> exif_size ;
463
+ uint8_t * old_data = jxl -> exif_data ;
464
+ size_t new_size = 0 ;
465
+ uint8_t * new_data = NULL ;
466
+
467
+ if (old_size >= 4 ) {
468
+ /* Offset is stored in big-endian
469
+ */
470
+ size_t offset = (old_data [0 ] << 3 ) + (old_data [1 ] << 2 ) +
471
+ (old_data [2 ] << 1 ) + old_data [3 ];
472
+
473
+ if (offset < old_size - 4 ) {
474
+ old_data += 4 + offset ;
475
+ old_size -= 4 + offset ;
476
+
477
+ new_size = old_size + 6 ;
478
+ new_data = g_malloc0 (new_size );
479
+
480
+ if (!new_data ) {
481
+ vips_error (class -> nickname , "%s" , _ ("out of memory" ));
482
+ return -1 ;
483
+ }
484
+
485
+ memcpy (new_data , "Exif\0\0" , 6 );
486
+ memcpy (new_data + 6 , old_data , old_size );
487
+ }
488
+ }
489
+
490
+ if (!new_data )
491
+ g_warning ("%s: invalid data in EXIF box" , class -> nickname );
492
+
493
+ g_free (jxl -> exif_data );
494
+
495
+ jxl -> exif_data = new_data ;
496
+ jxl -> exif_size = new_size ;
497
+
498
+ return 0 ;
499
+ }
500
+
393
501
static int
394
502
vips_foreign_load_jxl_set_header (VipsForeignLoadJxl * jxl , VipsImage * out )
395
503
{
@@ -484,6 +592,24 @@ vips_foreign_load_jxl_set_header(VipsForeignLoadJxl *jxl, VipsImage *out)
484
592
jxl -> icc_size = 0 ;
485
593
}
486
594
595
+ if (jxl -> exif_data &&
596
+ jxl -> exif_size > 0 ) {
597
+ vips_image_set_blob (out , VIPS_META_EXIF_NAME ,
598
+ (VipsCallbackFn ) vips_area_free_cb ,
599
+ jxl -> exif_data , jxl -> exif_size );
600
+ jxl -> exif_data = NULL ;
601
+ jxl -> exif_size = 0 ;
602
+ }
603
+
604
+ if (jxl -> xmp_data &&
605
+ jxl -> xmp_size > 0 ) {
606
+ vips_image_set_blob (out , VIPS_META_XMP_NAME ,
607
+ (VipsCallbackFn ) vips_area_free_cb ,
608
+ jxl -> xmp_data , jxl -> xmp_size );
609
+ jxl -> xmp_data = NULL ;
610
+ jxl -> xmp_size = 0 ;
611
+ }
612
+
487
613
vips_image_set_int (out ,
488
614
VIPS_META_ORIENTATION , jxl -> info .orientation );
489
615
@@ -499,6 +625,7 @@ vips_foreign_load_jxl_header(VipsForeignLoad *load)
499
625
VipsForeignLoadJxl * jxl = (VipsForeignLoadJxl * ) load ;
500
626
501
627
JxlDecoderStatus status ;
628
+ JXL_BOOL decompress_boxes = JXL_TRUE ;
502
629
503
630
#ifdef DEBUG
504
631
printf ("vips_foreign_load_jxl_header:\n" );
@@ -510,12 +637,17 @@ vips_foreign_load_jxl_header(VipsForeignLoad *load)
510
637
JxlDecoderRewind (jxl -> decoder );
511
638
if (JxlDecoderSubscribeEvents (jxl -> decoder ,
512
639
JXL_DEC_COLOR_ENCODING |
513
- JXL_DEC_BASIC_INFO )) {
640
+ JXL_DEC_BASIC_INFO |
641
+ JXL_DEC_BOX |
642
+ JXL_DEC_FRAME )) {
514
643
vips_foreign_load_jxl_error (jxl ,
515
644
"JxlDecoderSubscribeEvents" );
516
645
return -1 ;
517
646
}
518
647
648
+ if (JxlDecoderSetDecompressBoxes (jxl -> decoder , JXL_TRUE ) != JXL_DEC_SUCCESS )
649
+ decompress_boxes = JXL_FALSE ;
650
+
519
651
if (vips_foreign_load_jxl_fill_input (jxl , 0 ))
520
652
return -1 ;
521
653
JxlDecoderSetInput (jxl -> decoder ,
@@ -530,6 +662,49 @@ vips_foreign_load_jxl_header(VipsForeignLoad *load)
530
662
"JxlDecoderProcessInput" );
531
663
return -1 ;
532
664
665
+ case JXL_DEC_BOX :
666
+ /* Flush previous box data if any
667
+ */
668
+ if (vips_foreign_load_jxl_release_box_buffer (jxl ))
669
+ return -1 ;
670
+
671
+ JxlBoxType type ;
672
+ if (JxlDecoderGetBoxType (
673
+ jxl -> decoder , type , decompress_boxes ) != JXL_DEC_SUCCESS ) {
674
+ vips_foreign_load_jxl_error (jxl , "JxlDecoderGetBoxType" );
675
+ return -1 ;
676
+ }
677
+
678
+ #ifdef DEBUG
679
+ const char type_s [] = { type [0 ], type [1 ], type [2 ], type [3 ], 0 };
680
+ printf ("vips_foreign_load_jxl_header found box %s\n" , type_s );
681
+ #endif /*DEBUG*/
682
+
683
+ if (!memcmp (type , "Exif" , 4 )) {
684
+ jxl -> box_size = & jxl -> exif_size ;
685
+ jxl -> box_data = & jxl -> exif_data ;
686
+ }
687
+ else if (!memcmp (type , "xml " , 4 )) {
688
+ jxl -> box_size = & jxl -> xmp_size ;
689
+ jxl -> box_data = & jxl -> xmp_data ;
690
+ }
691
+ else {
692
+ jxl -> box_size = NULL ;
693
+ jxl -> box_data = NULL ;
694
+ }
695
+
696
+ if (vips_foreign_load_jxl_set_box_buffer (jxl ))
697
+ return -1 ;
698
+
699
+ break ;
700
+
701
+ case JXL_DEC_BOX_NEED_MORE_OUTPUT :
702
+ if (vips_foreign_load_jxl_release_box_buffer (jxl ) ||
703
+ vips_foreign_load_jxl_set_box_buffer (jxl ))
704
+ return -1 ;
705
+
706
+ break ;
707
+
533
708
case JXL_DEC_BASIC_INFO :
534
709
if (JxlDecoderGetBasicInfo (jxl -> decoder ,
535
710
& jxl -> info )) {
@@ -595,10 +770,18 @@ vips_foreign_load_jxl_header(VipsForeignLoad *load)
595
770
default :
596
771
break ;
597
772
}
598
- /* JXL_DEC_COLOR_ENCODING is always the last status signal before
773
+ /* JXL_DEC_FRAME is always the last status signal before
599
774
* pixel decoding starts.
600
775
*/
601
- } while (status != JXL_DEC_COLOR_ENCODING );
776
+ } while (status != JXL_DEC_FRAME );
777
+
778
+ /* Flush box data if any
779
+ */
780
+ if (vips_foreign_load_jxl_release_box_buffer (jxl ))
781
+ return -1 ;
782
+
783
+ if (vips_foreign_load_jxl_fix_exif (jxl ))
784
+ return -1 ;
602
785
603
786
if (vips_foreign_load_jxl_set_header (jxl , load -> out ))
604
787
return -1 ;
0 commit comments