@@ -104,8 +104,12 @@ typedef struct _VipsForeignLoadJxl {
104
104
uint8_t * xmp_data ;
105
105
106
106
int frame_count ;
107
- int * delay ;
108
- int delay_count ;
107
+ GArray * delay ;
108
+
109
+ /* JXL multipage and animated images are the same, but multipage has
110
+ * all the frame delays set to -1 (duration 0xffffffff).
111
+ */
112
+ gboolean is_animated ;
109
113
110
114
/* The current accumulated frame as a VipsImage. These are the pixels
111
115
* we send to the output. It's a info->xsize * info->ysize memory
@@ -166,7 +170,7 @@ vips_foreign_load_jxl_dispose(GObject *gobject)
166
170
VIPS_FREE (jxl -> icc_data );
167
171
VIPS_FREE (jxl -> exif_data );
168
172
VIPS_FREE (jxl -> xmp_data );
169
- VIPS_FREE ( jxl -> delay );
173
+ VIPS_FREEF ( g_array_unref , jxl -> delay );
170
174
VIPS_UNREF (jxl -> frame );
171
175
VIPS_UNREF (jxl -> source );
172
176
@@ -492,8 +496,7 @@ vips_foreign_load_jxl_read_frame(VipsForeignLoadJxl *jxl, VipsImage *frame,
492
496
int skip = frame_no - jxl -> frame_no - 1 ;
493
497
if (skip > 0 ) {
494
498
#ifdef DEBUG_VERBOSE
495
- printf ("vips_foreign_load_jxl_read_frame: skipping %d frames\n" ,
496
- skip );
499
+ printf ("vips_foreign_load_jxl_read_frame: skipping %d frames\n" , skip );
497
500
#endif /*DEBUG_VERBOSE*/
498
501
JxlDecoderSkipFrames (jxl -> decoder , skip );
499
502
jxl -> frame_no += skip ;
@@ -504,8 +507,7 @@ vips_foreign_load_jxl_read_frame(VipsForeignLoadJxl *jxl, VipsImage *frame,
504
507
do {
505
508
switch ((status = vips_foreign_load_jxl_process (jxl ))) {
506
509
case JXL_DEC_ERROR :
507
- vips_foreign_load_jxl_error (jxl ,
508
- "JxlDecoderProcessInput" );
510
+ vips_foreign_load_jxl_error (jxl , "JxlDecoderProcessInput" );
509
511
return -1 ;
510
512
511
513
case JXL_DEC_FRAME :
@@ -514,24 +516,19 @@ vips_foreign_load_jxl_read_frame(VipsForeignLoadJxl *jxl, VipsImage *frame,
514
516
515
517
case JXL_DEC_NEED_IMAGE_OUT_BUFFER :
516
518
if (JxlDecoderImageOutBufferSize (jxl -> decoder ,
517
- & jxl -> format ,
518
- & buffer_size )) {
519
+ & jxl -> format , & buffer_size )) {
519
520
vips_foreign_load_jxl_error (jxl ,
520
521
"JxlDecoderImageOutBufferSize" );
521
522
return -1 ;
522
523
}
523
- if (buffer_size !=
524
- VIPS_IMAGE_SIZEOF_IMAGE (frame )) {
525
- vips_error (class -> nickname ,
526
- "%s" , _ ("bad buffer size" ));
524
+ if (buffer_size != VIPS_IMAGE_SIZEOF_IMAGE (frame )) {
525
+ vips_error (class -> nickname , "%s" , _ ("bad buffer size" ));
527
526
return -1 ;
528
527
}
529
- if (JxlDecoderSetImageOutBuffer (jxl -> decoder ,
530
- & jxl -> format ,
528
+ if (JxlDecoderSetImageOutBuffer (jxl -> decoder , & jxl -> format ,
531
529
VIPS_IMAGE_ADDR (frame , 0 , 0 ),
532
530
VIPS_IMAGE_SIZEOF_IMAGE (frame ))) {
533
- vips_foreign_load_jxl_error (jxl ,
534
- "JxlDecoderSetImageOutBuffer" );
531
+ vips_foreign_load_jxl_error (jxl , "JxlDecoderSetImageOutBuffer" );
535
532
return -1 ;
536
533
}
537
534
break ;
@@ -551,8 +548,7 @@ vips_foreign_load_jxl_read_frame(VipsForeignLoadJxl *jxl, VipsImage *frame,
551
548
552
549
/* We didn't find the required frame
553
550
*/
554
- vips_error (class -> nickname ,
555
- "%s" , _ ("not enough frames" ));
551
+ vips_error (class -> nickname , "%s" , _ ("not enough frames" ));
556
552
return -1 ;
557
553
}
558
554
@@ -633,8 +629,7 @@ vips_foreign_load_jxl_set_header(VipsForeignLoadJxl *jxl, VipsImage *out)
633
629
634
630
if (jxl -> info .xsize >= VIPS_MAX_COORD ||
635
631
jxl -> info .ysize >= VIPS_MAX_COORD ) {
636
- vips_error (class -> nickname ,
637
- "%s" , _ ("image size out of bounds" ));
632
+ vips_error (class -> nickname , "%s" , _ ("image size out of bounds" ));
638
633
return -1 ;
639
634
}
640
635
@@ -704,27 +699,26 @@ vips_foreign_load_jxl_set_header(VipsForeignLoadJxl *jxl, VipsImage *out)
704
699
if (jxl -> page < 0 ||
705
700
jxl -> n <= 0 ||
706
701
jxl -> page + jxl -> n > jxl -> frame_count ) {
707
- vips_error (class -> nickname ,
708
- "%s" , _ ("bad page number" ));
702
+ vips_error (class -> nickname , "%s" , _ ("bad page number" ));
709
703
return -1 ;
710
704
}
711
705
712
706
vips_image_set_int (out , VIPS_META_N_PAGES , jxl -> frame_count );
713
707
714
708
if (jxl -> n > 1 )
715
- vips_image_set_int (out ,
716
- VIPS_META_PAGE_HEIGHT , jxl -> info .ysize );
709
+ vips_image_set_int (out , VIPS_META_PAGE_HEIGHT , jxl -> info .ysize );
717
710
718
- g_assert (jxl -> delay_count >= jxl -> frame_count );
719
- vips_image_set_array_int (out ,
720
- "delay" , jxl -> delay , jxl -> frame_count );
711
+ if (jxl -> is_animated ) {
712
+ int * delay = (int * ) jxl -> delay -> data ;
721
713
722
- /* gif uses centiseconds for delays
723
- */
724
- vips_image_set_int (out , "gif-delay" ,
725
- VIPS_RINT (jxl -> delay [0 ] / 10.0 ));
714
+ vips_image_set_array_int (out , "delay" , delay , jxl -> frame_count );
715
+
716
+ /* gif uses centiseconds for delays
717
+ */
718
+ vips_image_set_int (out , "gif-delay" , VIPS_RINT (delay [0 ] / 10.0 ));
726
719
727
- vips_image_set_int (out , "loop" , jxl -> info .animation .num_loops );
720
+ vips_image_set_int (out , "loop" , jxl -> info .animation .num_loops );
721
+ }
728
722
}
729
723
else {
730
724
jxl -> n = 1 ;
@@ -759,32 +753,28 @@ vips_foreign_load_jxl_set_header(VipsForeignLoadJxl *jxl, VipsImage *out)
759
753
if (jxl -> icc_data &&
760
754
jxl -> icc_size > 0 ) {
761
755
vips_image_set_blob (out , VIPS_META_ICC_NAME ,
762
- (VipsCallbackFn ) vips_area_free_cb ,
763
- jxl -> icc_data , jxl -> icc_size );
756
+ (VipsCallbackFn ) vips_area_free_cb , jxl -> icc_data , jxl -> icc_size );
764
757
jxl -> icc_data = NULL ;
765
758
jxl -> icc_size = 0 ;
766
759
}
767
760
768
761
if (jxl -> exif_data &&
769
762
jxl -> exif_size > 0 ) {
770
763
vips_image_set_blob (out , VIPS_META_EXIF_NAME ,
771
- (VipsCallbackFn ) vips_area_free_cb ,
772
- jxl -> exif_data , jxl -> exif_size );
764
+ (VipsCallbackFn ) vips_area_free_cb , jxl -> exif_data , jxl -> exif_size );
773
765
jxl -> exif_data = NULL ;
774
766
jxl -> exif_size = 0 ;
775
767
}
776
768
777
769
if (jxl -> xmp_data &&
778
770
jxl -> xmp_size > 0 ) {
779
771
vips_image_set_blob (out , VIPS_META_XMP_NAME ,
780
- (VipsCallbackFn ) vips_area_free_cb ,
781
- jxl -> xmp_data , jxl -> xmp_size );
772
+ (VipsCallbackFn ) vips_area_free_cb , jxl -> xmp_data , jxl -> xmp_size );
782
773
jxl -> xmp_data = NULL ;
783
774
jxl -> xmp_size = 0 ;
784
775
}
785
776
786
- vips_image_set_int (out ,
787
- VIPS_META_ORIENTATION , jxl -> info .orientation );
777
+ vips_image_set_int (out , VIPS_META_ORIENTATION , jxl -> info .orientation );
788
778
789
779
vips_image_set_int (out , VIPS_META_BITS_PER_SAMPLE ,
790
780
jxl -> info .bits_per_sample );
@@ -795,7 +785,6 @@ vips_foreign_load_jxl_set_header(VipsForeignLoadJxl *jxl, VipsImage *out)
795
785
static int
796
786
vips_foreign_load_jxl_header (VipsForeignLoad * load )
797
787
{
798
- VipsObjectClass * class = VIPS_OBJECT_GET_CLASS (load );
799
788
VipsForeignLoadJxl * jxl = (VipsForeignLoadJxl * ) load ;
800
789
801
790
JxlDecoderStatus status ;
@@ -815,8 +804,7 @@ vips_foreign_load_jxl_header(VipsForeignLoad *load)
815
804
JXL_DEC_BASIC_INFO |
816
805
JXL_DEC_BOX |
817
806
JXL_DEC_FRAME )) {
818
- vips_foreign_load_jxl_error (jxl ,
819
- "JxlDecoderSubscribeEvents" );
807
+ vips_foreign_load_jxl_error (jxl , "JxlDecoderSubscribeEvents" );
820
808
return -1 ;
821
809
}
822
810
@@ -825,8 +813,7 @@ vips_foreign_load_jxl_header(VipsForeignLoad *load)
825
813
826
814
if (vips_foreign_load_jxl_fill_input (jxl , 0 ) < 0 )
827
815
return -1 ;
828
- JxlDecoderSetInput (jxl -> decoder ,
829
- jxl -> input_buffer , jxl -> bytes_in_buffer );
816
+ JxlDecoderSetInput (jxl -> decoder , jxl -> input_buffer , jxl -> bytes_in_buffer );
830
817
831
818
jxl -> frame_count = 0 ;
832
819
@@ -919,21 +906,17 @@ vips_foreign_load_jxl_header(VipsForeignLoad *load)
919
906
#ifndef HAVE_LIBJXL_0_9
920
907
& jxl -> format ,
921
908
#endif
922
- JXL_COLOR_PROFILE_TARGET_DATA ,
923
- & jxl -> icc_size )) {
909
+ JXL_COLOR_PROFILE_TARGET_DATA , & jxl -> icc_size )) {
924
910
vips_foreign_load_jxl_error (jxl ,
925
911
"JxlDecoderGetICCProfileSize" );
926
912
return -1 ;
927
913
}
928
914
929
915
#ifdef DEBUG
930
- printf (
931
- "vips_foreign_load_jxl_header: "
932
- "%zd byte profile\n" ,
916
+ printf ("vips_foreign_load_jxl_header: %zd byte profile\n" ,
933
917
jxl -> icc_size );
934
918
#endif /*DEBUG*/
935
- if (!(jxl -> icc_data = vips_malloc (NULL ,
936
- jxl -> icc_size )))
919
+ if (!(jxl -> icc_data = vips_malloc (NULL , jxl -> icc_size )))
937
920
return -1 ;
938
921
939
922
if (JxlDecoderGetColorAsICCProfile (jxl -> decoder ,
@@ -950,42 +933,41 @@ vips_foreign_load_jxl_header(VipsForeignLoad *load)
950
933
951
934
case JXL_DEC_FRAME :
952
935
if (JxlDecoderGetFrameHeader (jxl -> decoder , & h ) != JXL_DEC_SUCCESS ) {
953
- vips_foreign_load_jxl_error (jxl ,
954
- "JxlDecoderGetFrameHeader" );
936
+ vips_foreign_load_jxl_error (jxl , "JxlDecoderGetFrameHeader" );
955
937
return -1 ;
956
938
}
957
939
958
940
if (jxl -> info .have_animation ) {
959
- if (jxl -> delay_count <= jxl -> frame_count ) {
960
- jxl -> delay_count += 128 ;
961
- int * new_delay = g_try_realloc (jxl -> delay ,
962
- jxl -> delay_count * sizeof (int ));
963
- if (!new_delay ) {
964
- vips_error (class -> nickname , "%s" , _ ("out of memory" ));
965
- return -1 ;
966
- }
967
- jxl -> delay = new_delay ;
968
- }
969
-
970
- jxl -> delay [jxl -> frame_count ] = VIPS_RINT (1000.0 * h .duration *
971
- jxl -> info .animation .tps_denominator /
972
- jxl -> info .animation .tps_numerator );
941
+ // tick duration in seconds
942
+ double tick = (double ) jxl -> info .animation .tps_denominator /
943
+ jxl -> info .animation .tps_numerator ;
944
+ // this duration in ms
945
+ int ms = VIPS_RINT (1000.0 * h .duration * tick );
946
+ // h.duration of 0xffffffff is used for multipage JXL ... map
947
+ // this to -1 in delay
948
+ int duration = h .duration == 0xffffffff ? -1 : ms ;
949
+
950
+ jxl -> delay = g_array_append_vals (jxl -> delay , & duration , 1 );
973
951
}
974
952
975
953
jxl -> frame_count ++ ;
976
954
977
- /* This is the last frame, we can stop right here
978
- */
979
- if (h .is_last || !jxl -> info .have_animation )
980
- status = JXL_DEC_SUCCESS ;
981
-
982
955
break ;
983
956
984
957
default :
985
958
break ;
986
959
}
987
960
} while (status != JXL_DEC_SUCCESS );
988
961
962
+ /* Detect JXL multipage (rather than animated).
963
+ */
964
+ int * delay = (int * ) jxl -> delay -> data ;
965
+ for (int i = 0 ; i < jxl -> delay -> len ; i ++ )
966
+ if (delay [i ] != -1 ) {
967
+ jxl -> is_animated = TRUE;
968
+ break ;
969
+ }
970
+
989
971
/* Flush box data if any
990
972
*/
991
973
if (vips_foreign_load_jxl_release_box_buffer (jxl ))
@@ -1113,6 +1095,7 @@ static void
1113
1095
vips_foreign_load_jxl_init (VipsForeignLoadJxl * jxl )
1114
1096
{
1115
1097
jxl -> n = 1 ;
1098
+ jxl -> delay = g_array_new (FALSE, FALSE, sizeof (int ));
1116
1099
}
1117
1100
1118
1101
typedef struct _VipsForeignLoadJxlFile {
0 commit comments